Class C++ 中的模板和友谊

Class Templates and Friendship in C++

我想了解模板和友谊在 C++ 中的工作原理。所以 looking/trying 我自己举了一些例子。下面给出了一个我无法理解的例子:

版本 1

#include <iostream>

using namespace std;


//template<typename T> void func4();
//template<typename T> class NAME;
//   template<typename T> std::ostream& operator<< (std::ostream&, NAME<T> const&);
template<typename T>
class NAME {
   

    friend void func4<T>();
    friend std::ostream& operator<< <T> (std::ostream&, NAME<T> const&);
};
int main()
{
   cout << "Hello World" << endl; 
 
   return 0;
}

以上版本1报错如下:

prog.cc:13:17: error: variable or field 'func4' declared void
   13 |     friend void func4<T>();
      |                 ^~~~~
prog.cc:13:17: error: expected ';' at end of member declaration
   13 |     friend void func4<T>();
      |                 ^~~~~
      |                      ;
prog.cc:13:22: error: expected unqualified-id before '<' token
   13 |     friend void func4<T>();
      |                      ^

我的 第一个问题 是,即使我已经对 func4operator<< 模板函数的前向声明进行了评论,那我怎么才能只得到仅 func4 出错?这就是 operator<<.

没有错误的原因

请注意,我知道如果我们想与模板的特定实例成为朋友,我们需要前向声明。那是为了

friend void func4<T>();

为了工作,我们需要注释掉语句

template<typename T> void func4();

同样适用于

friend std::ostream& operator<< <T> (std::ostream&, NAME<T> const&);

为了工作,我们需要在程序开始时注释掉相应的前向声明。但是当我只注释掉 template<typename T> void func4(); 语句时,程序可以运行并且 operator<< 没有错误。同样,为什么我们没有收到 operator<< 的错误,即使我没有注释掉相应的前向声明。

我的第二个问题是这样的说法

friend void func4<T>();

friend void func4<>();

同理,是语句

friend std::ostream& operator<< <T> (std::ostream&, NAME<T> const&);

friend std::ostream& operator<< <> (std::ostream&, NAME<T> const&);

我的想法是 friend void func4<>(); 语句是使用语句 template <typename T> void func4(); 声明的函数模板的特化。同时,当我们写 friend void func4<T>(); 时,我们 显式 传递 T 所以在这种情况下,当我们 write/call func4<int>(); 函数模板 func4<int>会被实例化然后占用内存。另一方面,专用模板函数已经占用了一些内存,因为我们必须在调用它之前提供它的定义。那么有人可以解释一下当我们使用 <T> 和使用 <> 时是否存在 的差异。总之,在我看来,在 <> 的情况下,将使用用户提供的专业化,而在 <T> 的情况下,将在我们调用 func4() 时创建一个新的 intiantion。所以这是有区别的,因为在 <> 的情况下,我们必须提供一个已经占用一些 space(内存)的定义,而在 <T> 的情况下, func4 只会占用 space(内存)当我们use/call它。这是正确的结论吗?

我的第三个问题是我read

There is no way to explicitly specify template arguments to overloaded operators, conversion functions, and constructors, because they are called without the use of the function name.

根据上面引用的语句,我们不能显式地为重载运算符指定模板参数,但是如何写 friend std::ostream& operator<< <T> (std::ostream&, NAME<T> const&); 因为它重载运算符 << .

回答 1 operator<< 没有错误,因为您使用了 using namespace std; 并且在 std 命名空间中已经重载了 operator<<.

回答 3 引用的语句谈论调用(即使用)重载运算符而不是 defining/declaring 他们

回答2

版本 1

#include <iostream>

template<typename T> void func4();
template<typename T>
class NAME {
   

    //friend void func4<>();//this does not work because there is no way to deduce the template arguments
    friend void func4<T>();//this works because here we have explicitly passed T as the template argument

};
int main()
{
   std::cout << "Hello World" << std::endl; 
   NAME<int> n;
   return 0;
}

在上面的版本 1 中,我们有一个函数模板 func4<>。现在,当您编写 friend void func<T>();friend void func<>(); 时,您正在使 class 模板 NAME<> 成为 完全专业化 友元。 friend void func<>(); 不起作用的原因是模板参数推导在这种情况下无法起作用。当你写 friend void func<T>(); 时,你已经显式地传递了模板参数,所以这是有效的。这也意味着两者(friend void func<T>friend void func<>();)本质上是模板函数 func<> 的相同特化。这将从我的下一个示例版本 2 中更加清楚。

版本 2

#include <iostream>

template<typename T> class NAME;
template<typename T> std::ostream& operator<<(std::ostream&, const NAME<T>& );
template<typename T>
class NAME {
   
    //both of the below friend declarations are equivalent
    friend std::ostream& operator<< <T>(std::ostream&, const NAME<T>&);//this works 
    friend std::ostream& operator<< <>(std::ostream&, const NAME<T>&);//this works as well becasue of template argument deduction
    
};
int main()
{
   std::cout << "Hello World" << std::endl; 
   NAME<int> n;
   return 0;
}

在上面的版本 2 中,我们有一个重载的运算符函数模板。 friend std::ostream& operator<< <T>(std::ostream&, const NAME<T>&);friend std::ostream& operator<< <>(std::ostream&, const NAME<T>&); 都是 等价的 并且是重载模板 operator<< <T> 的相同特化。它们之间唯一的区别是第二个使用模板参数推导。这是可能的,因为与版本 1 不同,这次我们有一个依赖于 T 的函数参数,因此模板参数推导可以工作。这在函数模板 func4<> 的情况下是不可能的,因为该函数不采用任何依赖于模板参数的参数。