友元模板函数类定义

Friend template function in-class definition

本文关键字:定义 函数 友元      更新时间:2023-10-16
我不知道

,为什么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。

问题是由我的回答引起的:类中友元运算符似乎不参与重载解决,答案中有标准的引用,指出这样的定义应该在类范围内,如果函数不是模板 - gcc 拒绝这个代码,这是对的。非常感谢您的回答和标准引用,证明 gcc 是正确的(或不是(。

我会说GCC错误地接受了这一点。引用C++11,强调我的:

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

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

依赖于参数的查找,3.4.2/2:

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

  • 如果T是枚举类型,则其关联的命名空间是在其中定义它的命名空间。如果是 类成员,其关联的类是成员的类;否则它没有关联的类。

3.4.2/4:

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

  • 在关联类中声明的任何命名空间范围的友元函数或友元函数模板都是 在各自的命名空间中可见,即使在普通查找期间它们不可见 (11.3(。

基于上述,我推断FooValueT(FooValueT::ONEFooValueT::TWO的类型(已::为关联的命名空间,但没有关联的类(因为它是枚举(。因此,在 ADL 期间,不应考虑类模板ImplAdd中定义的友元函数。