在模板类之外定义友元函数的正确方法是什么?

alf*_*lfC 4 c++ forward-declaration friend-function argument-dependent-lookup c++11

如果我有一个普通的类,我可以在类中“注入”一个非自由的友元函数。(除其他外,只能由 ADL 找到)。

情况1:

class A{
  double p_;
  friend double f(A const& a){return a.p_;}
};
Run Code Online (Sandbox Code Playgroud)

如果这是一个模板类,我可以这样做:

案例2:

template<class T>
class A{
  double p_;
  friend double f(A const& a){return a.p_;} // apparently A const& is a synomyn for A<T> const&
};
Run Code Online (Sandbox Code Playgroud)

现在假设我需要f根据稍后需要定义的类来实现。我在这种情况下尝试这样做:

案例3:

template<class T>
class A{
    double p_;
    friend double f(A const& a);
};
...
Run Code Online (Sandbox Code Playgroud)

这已经给出了一个警告:“警告:朋友声明 'double f(const A&)' 声明了一个非模板函数 [-Wnon-template-friend]”。

按照编译器的建议,我可以这样做:

template<class T> class A;

template<class T> double f(A<T> const& a);

template<class T>
class A{
    double p_;
    friend double f<>(A const& a);
};

template<class T> double f(A<T> const& a){return a.p_;}
Run Code Online (Sandbox Code Playgroud)

这需要更多的代码,我什至不确定它是否 100% 等价于我想要的情况 2,因为现在我有一个真正免费的函数,它恰好是一个朋友而不是一个注入的朋友。

情况 3 可以修改为 100% 等价于情况 2 并且仍然具有f类外的定义吗?换句话说,可以注入一个定义在类之外的友元函数吗?


我也试过这个,它给出了一个编译器错误:

template<class T>
class A{
    double p_;
    friend double f(A<T> const& a);
};

template<class T> double A<T>::f(A<T> const& a){return a.p_;}
Run Code Online (Sandbox Code Playgroud)

这个答案找到了相同的解决方案,但没有回答关于 case 3 等效于 case 2 的问题。在模板类中编写友元函数声明的正确方法是什么?

Jar*_*d42 5

友元函数有特殊的可见性规则(ADL 的特殊情况),因此在类外定义函数与在类内定义是不同的。

此外,在情况 2 中,该函数不是模板。即使每个模板都有一个。因此,要在类之外实现它,您必须friend double f(A<T> const& a);为每个T.

该建议是最接近的解决方法:

  • 您的功能(仅专业化)是朋友。
  • 但是您的函数是模板(因此应该进行推导:
    使用friend double f(A<T> const& a, T);(情况 2),f(A<float>{}, 42);会成功,
    friend double f<>(A<T> const& a, T);不会成功
    T将是floatforA<float>intfor 42))

  • 你的函数是在外面声明的,所以它的可见性是“不同的”

现在假设我需要f根据稍后需要定义的类来实现。我在这种情况下尝试这样做:

其他解决方法是声明一个可以完成这项工作的私有方法,它允许您在类中定义友元。然后可以稍后定义该私有方法:

template<class T>
class A{
    double p_;

    double do_f() const;
    friend double f(A const& a){return a.do_f();}
};

// Thing needed by A<T>::do_f

template<class T>
double A<T>::do_f() const
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

如果返回类型是不完整的类型,您必须对auto返回做一个技巧(这适用于 g++11 和 clang++11)。

template<class T> class A;
class B;

template<class T>
class A{
    B do_f() const;
    friend auto f(A const& a){return a.do_f();} // not friend B f(...
};

class B{};

template<class T> B A<T>::do_f() const{return B{};}

int main(){A<double> a; f(a);}
Run Code Online (Sandbox Code Playgroud)