为啥 g++ 需要模板类及其友元函数的定义?

Posted

技术标签:

【中文标题】为啥 g++ 需要模板类及其友元函数的定义?【英文标题】:why does g++ need both definitions of template class and its friend function?为什么 g++ 需要模板类及其友元函数的定义? 【发布时间】:2017-10-28 06:19:55 【问题描述】:

我想为模板类写一个友元函数。 在视觉工作室,我可以忽略预定义。 但在 g++ 中,它是强制性的。 为什么?

#include <iostream>
using namespace std;
// g++ needs, vs do not needs
template <class T>
class A;

template <class T>
ostream & operator<<(ostream & c, const A<T> & v);
//- end of g++ needs

template <class T>
class A 
    T _v;
public:
    A() 
    A(T v) : _v(v) 
    friend ostream & operator<<<T>(ostream & c, const A<T> & v);
;
template <class T>
ostream & operator<<(ostream & c, const A<T> & v) 
    c << v._v; return c;

【问题讨论】:

我最好的猜测是 VisualStudio “帮助”你比 g++ 多一点,只是为你添加它。具有讽刺意味的是,g++ 似乎为您提供了更好的编译错误描述。我所说的“帮助”是指当我在代码中输入 File.txt 时,VS 打开了 file.txt,但 g++ 说文件不存在并且无法打开。 在 g++ 中,friend 函数声明(或定义?)需要原型。要制作原型,您又需要模板类的前向声明。在 g++ 中学习过一次后,我想知道这些天我的代码在 VS without 中是否可以工作(但我没有深入挖掘。)现在,我很清楚了。 (感谢您的提问。) @BrianW 我相信,您提到的关于file.txtFile.txt 的示例只是操作系统问题。在 Windows 上,文件名通常不区分大小写。 (如果应用程序不使用系统函数,它可能会破坏它。)当然,在 Linux 上,文件名总是区分大小写的(但是,如果不使用系统函数,应用程序可能会破坏它)。但是,总的来说,我同意您的看法:VS 关于 C++ 标准要宽松得多(方便?)。当我们将应用程序移植到我们在 Windows/VS 中开发的 Linux/g++ 时,我总是会遇到这个问题(尽管我们总是尝试制作可移植的代码)。 好点@Scheff,我没有考虑 Windows 与 Linux 的事情,并认为这只是 IDE。 @YunfeiDuan 请不要发布“谢谢”cmets。如果您想说“谢谢”,请为答案投票。 (这可以让作者获得 10 点声誉,一定会受到欢迎。)请不要忘记 accept 您认为解决问题的答案(作者 +15)。 【参考方案1】:

因为

friend ostream & operator<<<T>(ostream & c, const A<T> & v);

的特化
template <class T>
ostream & operator<<(ostream & c, const A<T> & v);

你需要先声明,然后

A<T>

部分意味着你必须在操作符声明之前声明它

template <class T>
class A;

所以 VS 可能是错误的C++14

14.5.4 Friends [temp.friend] 

举个例子

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);

template<class T> class task 
  friend void next_time();
  friend void process(task<T>*);
  friend task<T>* preempt<T>(task<T>*);
  template<class C> friend int func(C);

  friend class task<int>;
  template<class P> friend class frd;
;

您的示例适合第三个朋友声明。

【讨论】:

【参考方案2】:

MSVC 模板代码被彻底破坏。在过去的几年里,他们一直在重建它,但它仍然包含你所观察到的怪癖。这是因为它被构建为几乎像宏一样,而不是 C++ 标准所要求的。

在 gcc 中,您可以通过在类的主体中内联定义 &lt;&lt; 运算符来取消前向声明:

friend std::ostream& operator<<(std::ostream& c, const A& v)
  c << v._v;
  return c;

这样做的好处是&lt;&lt; 不再是template,而是为模板A 的每个实例创建的不同的非模板。在某些情况下,这往往效果更好。

【讨论】:

以上是关于为啥 g++ 需要模板类及其友元函数的定义?的主要内容,如果未能解决你的问题,请参考以下文章

友元函数和友元类

C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板和友元

为啥在 A 类中声明为友元的函数必须在 B 类中定义为公共函数?

模板实现顺序表

为啥 PRIVATE 成员函数不能成为另一个类的友元函数?

友元函数