C++ 类定义中的友元模板函数

C++ 类定义中的友元模板函数,c++,c++11,gcc,language-lawyer,C++,C++11,Gcc,Language Lawyer,我不知道gcc为什么编译这段代码 #include <type_traits> template<class Type, class ValueT> class ImplAdd { template<typename T> friend typename std::enable_if<std::is_same<T, ValueT>::value, Type>::type operator+(T, T) {

我不知道gcc为什么编译这段代码

#include <type_traits>

template<class Type, class ValueT>
class ImplAdd
{
   template<typename T>
   friend typename std::enable_if<std::is_same<T, ValueT>::value, Type>::type 
   operator+(T, T)
   {
      return Type{};
   }
};

enum class FooValueT { ONE, ZERO };

class Foo : ImplAdd<Foo, FooValueT>
{
public:
   Foo() {}
   Foo(FooValueT) {}
};

struct A {};

int main()
{
   Foo f = FooValueT::ONE + FooValueT::ZERO;
}
#包括
模板
类ImplAdd
{
模板
好友类型名称std::enable_if::type
运算符+(T,T)
{
返回类型{};
}
};
枚举类FooValueT{ONE,ZERO};
类Foo:ImplAdd
{
公众:
Foo(){}
Foo(FooValueT){}
};
结构A{};
int main()
{
Foo f=FooValueT::ONE+FooValueT::ZERO;
}
clang和msvc没有编译,在我看来,他们是对的。它是GCC编译器中的bug吗?gcc的版本是4.8.2


这个问题是由我在问题中的答案引起的:,答案中引用了标准,指出这样的定义应该在类范围内,若函数不是模板-gcc,那个么拒绝这个代码,这是正确的。感谢您的回答,以及来自标准的引用,这证明了gcc是对的(或不是),我们非常感谢。

我认为gcc错误地接受了这一点。引用C++11,重点:

命名空间成员资格,7.3.1.2/3

在命名空间中首先声明的每个名称都是该命名空间的成员如果在非本地 类首先声明一个类或函数友元类或函数是最内层封闭 命名空间非限定查找(3.4.1)或限定查找(3.4.3)不会找到好友的名称,直到在该命名空间范围中提供匹配声明(在类定义之前或之后) 给予友谊)如果调用了友元函数,则可以通过考虑 来自和函数参数类型(3.4.2)关联的名称空间和类的函数。

参数相关查找,3.4.2/2:

对于函数调用中的每个参数类型
T
,都有一组零个或多个相关名称空间和一个 要考虑的零个或多个关联类的集合。名称空间和类的集合是确定的 完全由函数参数的类型(以及任何模板参数的名称空间)决定。 Typedef名称和用于指定类型的使用声明不属于此集合。一套 名称空间和类按以下方式确定:

  • 如果
    T
    是枚举类型,则其关联的命名空间就是定义它的命名空间。如果是 类成员,其关联的类是该成员的类否则它没有关联的类。
3.4.2/4:

在考虑关联的命名空间时,查找与 关联命名空间用作限定符(3.4.3.2),但以下情况除外:

  • 关联类中声明的任何命名空间作用域友元函数或友元函数模板都是 在它们各自的名称空间中可见,即使它们在普通查找过程中不可见(11.3)

基于上述原因,我认为
FooValueT
(类型为
FooValueT::ONE
FooValueT::TWO
)具有
::
作为关联的命名空间,但没有关联的类(因为它是一个枚举)。因此,在ADL过程中不应考虑类模板
ImplAdd
中定义的友元函数。

谢谢您的回答。第一句话在这里并不重要。您可能需要7.3.1.2[namespace.memdef]/p3:“如果非局部类中的友元声明首先声明了一个类或函数,则友元类或函数是最内层封闭命名空间的成员。通过非限定查找(3.4.1)或限定查找(3.4.3)无法找到友元的名称直到在该命名空间范围中提供匹配声明(在类定义之前或之后)。“@T.C.很好,谢谢!我知道有一段话更直接地阐述了这一点,但我不记得在哪里。我再加上。