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

时间:2018-10-10 21:46:28

标签: c++ c++11 forward-declaration argument-dependent-lookup friend-function

如果我有一个普通的课堂,我可以在课堂内“注入”一个非自由的朋友函数。 (除其他外,只有ADL才能找到它。)

情况1:

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

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

情况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&
};

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

情况3:

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

这已经发出警告:“警告:朋友声明'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_;}

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

案例3是否可以修改为与案例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_;}

此答案找到相同的解决方案,但没有回答关于案例3等同于案例2的问题。What is the right way to write friend function declarations in template class?

1 个答案:

答案 0 :(得分:3)

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

此外,在第2种情况下,该功能是 not 模板。即使每个模板都有一个。所以要在课外实施 您将必须为每个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对于float将是A<float>,对于int将是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
{
    // ...
}

如果返回类型为不完整类型,则必须使用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);}