依赖范围内的功能之友

时间:2016-05-09 12:11:31

标签: c++

此代码无效:

template <class T> struct A;

class C {
    template <class T> friend void A<T>::foo();
};

在GCC 6.1.0中它说:

error: member 'void A<T>::foo()' declared as friend before type 'A<T>' defined

     template <class T> friend void A<T>::foo();

Clang 3.8.0:

warning: dependent nested name specifier 'A<T>::' for friend class declaration 
is not supported; turning off access control for 'C' [-Wunsupported-friend]

Visual Studio 2015崩溃:

fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\template.cpp', line 8952)
        template <class T> friend void A<T>::foo();

更具体地说,是否需要在朋友声明之前定义A

  template <class T> struct A;

  class C {
    static void foo();
    template <class T> friend void A<T>::f();
  };

  template <class T> struct A {
    void f() { }
  };

如果是这样,为什么?

4 个答案:

答案 0 :(得分:4)

您引用A的成员函数foo。目前尚不知道此函数存在,因为您只转发声明A

换句话说,你必须在A<T>::foo之前声明C,因为GCC消息试图告诉你(另外两个是相当神秘的)。这意味着您必须在A之前声明C的完整界面,而不是仅向前声明它。

答案 1 :(得分:2)

解决方案

您可以通过以下方式将f声明为C的朋友:

class C;

template <class T> struct A {
  void f(C const &c);
};

class C {
  int i = 42;
public:
  static void foo();
  template <class T> friend void A<T>::f(C const &c);
};

template <class T> void A<T>::f(C const &c) { std::cout << c.i << std::endl; }

Live Demo

理由

根据标准§3.3.2/ p6声明点[basic.scope.pdecl]

  

在成员声明之后,成员名称可以   在同班的范围内被抬头看。 [注意:即使这样也是如此   这个班级是一个不完整的班级。例如,

struct X {
enum E { z = 16 };
int b[X::z]; // OK
};
     

- 结束说明]

诊断对GCC和CLANG都有误导性。你的代码的真正问题不是你试图访问不完整类型的定义,而是你只能在声明它之后才能引用类成员名。

答案 2 :(得分:1)

第一个问题是(我认为)来自§9.3/ 7 [class.mfct] ,可能还有标准中的其他一些地方(见下面的clang消息和101010的回答):

  

以前声明的成员函数可以在朋友声明中提及。

此问题类似于this question中的问题。

由于您未在A<T>::f之前声明C,因此您无法声明其中有C的朋友。

但是存在一个隐藏的问题behing clang的消息,如果你使A成为非模板化的类,则消息是不同的:

  

嵌套名称说明符中的不完整类型A

哪个更接近gcc消息而不是实际消息,这是因为clang的警告是关于其他事情的。根据§14.5.4/ 5 [temp.friend] ,该标准允许类模板的成员成为朋友,所以这必须是有效的:

template <typename T>
struct A {
    void f ();
};

class C {
    template <typename T>
    friend void A<T>::f(); // Ok, A<T>::f is already declared

    void private_member (); // See below
};

但是clang仍然抱怨:

  

警告:用于友元类声明的依赖嵌套名称说明符'A ::'   不受支持;关闭'C'[-Wunsupported-friend]

的访问控制

clang不支持此类friend声明,因此只需关闭 C的访问控制,这意味着:

C c;
c.private_member();

在任何地方都会“有效”,这可能不是你想要的。

答案 3 :(得分:0)

首先,如果需要使用实际的类类型,例如,如果需要将其用作基类,或者需要在类中使用类的方法,则类的前向声明是不够的。方法

因为在这里你尝试使用它的细节“foo()”,编译器无法知道什么是A :: foo()..编译器无法区分它是否是一个错字(或)实际函数..它需要在你可以使用之前知道A :: foo()的声明。

如果您仍然只想转发声明课程并使其成为朋友,请查看朋友课程是否适合您的情况

template <class T> struct A;

class C{
   template <class T> friend struct A;
};