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>();
| ^
我的 第一个问题 是,即使我已经对 func4
和 operator<<
模板函数的前向声明进行了评论,那我怎么才能只得到仅 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<>
的情况下是不可能的,因为该函数不采用任何依赖于模板参数的参数。
我想了解模板和友谊在 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>();
| ^
我的 第一个问题 是,即使我已经对 func4
和 operator<<
模板函数的前向声明进行了评论,那我怎么才能只得到仅 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<>
的情况下是不可能的,因为该函数不采用任何依赖于模板参数的参数。