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

What is the right way to define a friend function outside a template class?

如果我有一个正常的 class 我可以 "inject" 在 class 里面添加一个非自由的好友函数。 (除其他外,只能通过 ADL 找到)。

案例 1:

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

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

案例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&
};

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

案例 3:

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

这已经给出了警告:"warning: friend declaration ‘double f(const A&)’ declares a non-template function [-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_;}

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

是否可以将案例 3 修改为 100% 等同于案例 2,并且在 class 之外仍然有 f 的定义?换句话说,可以注入一个由 class?

定义的友元函数吗?

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

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 的问题。

友元函数具有特殊的可见性规则(ADL 的特殊情况),因此在 class 外部定义函数与在内部定义函数无论如何是不同的。

此外,在情况2中,函数不是模板。即使每个模板都有一个。所以要在 class 之外实现它, 您必须为每个 T.

实现每个 friend double f(A<T> const& a);

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

  • 你的功能(只有专业化)是朋友。
  • 但是你的函数是模板(所以推导应该发生:
    使用 friend double f(A<T> const& a, T);(案例 2),f(A<float>{}, 42); 会成功
    friend double f<>(A<T> const& a, T); 不会
    T 对于 A<float> 将是 float,对于 42 将是 int))

  • 你的函数是在外面声明的,所以它的可见性是"different".

Now suppose that I need to implement f in terms of a class that needs to be defined later. I such case I tried doing this:

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

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
{
    // ...
}

如果 return 类型是一个不完整的类型,你必须用 auto return 做一个技巧(这在 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);}