[英]Class Templates and Friendship in C++
我试图了解模板和友谊在 C++ 中是如何工作的。 所以自己寻找/尝试一些例子。 下面给出了一个我无法理解的示例:
版本 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
所以在这种情况下,当我们编写/调用func4<int>();
函数模板func4<int>
将被初始化,然后占用内存。 另一方面,专用模板函数已经占用了一些内存,因为我们必须在调用它之前提供它的定义。 那么有人可以解释一下我们使用<T>
和使用<>
时是否有区别。 总之,在我看来,在<>
的情况下,将使用用户提供的专业化,而在<T>
的情况下,当我们调用 func4() 时,将创建一个新的意图。 所以有区别,因为在<>
情况下,我们必须提供一个已经占用一些空间(内存)的定义,而在<T>
的情况下,func4 只会在我们使用/调用它时占用空间(内存)。 这是正确的结论吗?
我的第三个问题是我已经读过:
无法为重载运算符、转换函数和构造函数显式指定模板参数,因为它们的调用不使用函数名称。
根据上面引用的语句,我们不能为重载运算符显式指定模板参数,但是如何编写friend std::ostream& operator<< <T> (std::ostream&, NAME<T> const&);
因为它重载 operator << 。
答案 1 operator<<
没有错误,因为您使用了using namespace std;
并且在std
命名空间中已经有重载的operator<<
。
答案 3引用的语句谈到调用(即,使用)重载运算符而不是定义/声明它们
答案 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<>();
,您正在成为类模板 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<>
情况下是不可能的,因为该函数不接受任何依赖于模板参数的参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.