我不知道,为什么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;
}
clang和msvc没有编译,在我看来,他们是对的。它是GCC编译器中的错误吗? gcc的版本是4.8.2。
问题是由我的问题答案引起的:In-class friend operator doesn't seem to participate in overload resolution,答案中有标准引用,指出这样的定义应该在类范围内,如果函数不是模板 - gcc拒绝此代码,这是对的。感谢您的答案,以及标准的引用,证明gcc是对(或不对)非常感激。
答案 0 :(得分:3)
我会说GCC接受这个错误。引用C ++ 11,强调我的:
命名空间成员资格,7.3.1.2 / 3
首先在名称空间中声明的每个名称都是该名称空间的成员。 如果非本地的
friend
声明 class首先声明一个类或函数友元类或函数是最内层封闭的成员 命名空间。 非限定查找(3.4.1)或限定查找(3.4.3)找不到朋友的名称,直到在该命名空间范围内提供匹配声明(在类定义之前或之后) 给予友谊)。 如果调用了友元函数,则可以通过考虑的名称查找找到其名称 来自名称空间的函数和与函数参数类型相关的类(3.4.2)。 ...
依赖于参数的查找,3.4.2 / 2:
对于函数调用中的每个参数类型
T
,都有一组零个或多个关联的命名空间和一个 要考虑的零个或多个关联类的集合。确定名称空间和类的集合 完全由函数参数的类型(以及任何模板模板参数的命名空间)组成。 用于指定类型的Typedef名称和 using-declarations 对此集合没有贡献。套 命名空间和类的确定方式如下:
- ...
- 如果
T
是枚举类型,则其关联的命名空间是定义它的命名空间。如果是 类成员,其关联类是成员的类; 其他没有关联的课程。- ...
3.4.2 / 4:
在考虑关联的命名空间时,查找与执行时的查找相同 关联命名空间用作限定符(3.4.3.2),但以下情况除外:
- ...
- 任何命名空间范围的朋友函数或朋友函数模板在关联类中声明 即使它们在普通查找期间不可见(11.3),也可以在各自的命名空间中可见。
- ...
基于以上所述,我推断FooValueT
(FooValueT::ONE
和FooValueT::TWO
的类型)具有::
作为关联的命名空间,但没有关联的类(因为这是一个枚举)。因此,在ADL期间不应考虑在类模板ImplAdd
中定义的友元函数。