Sum*_*ndo 9 c++ templates visual-c++ friend-function
我与中描述的问题挣扎了这个问题(声明一个模板函数作为模板类的朋友),我相信第二个答案就是我想要做的(向前声明模板函数,然后将其命名一个专门为好友).我有一个问题,一个稍微不同的解决方案是否实际上是正确的,或者只是恰好在Visual C++ 2008中工作.
测试代码是:
#include <iostream>
// forward declarations
template <typename T>
class test;
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t);
template <typename T>
class test {
friend std::ostream& operator<< <T>(std::ostream &out, const test<T> &t);
// alternative friend declaration
// template <typename U>
// friend std::ostream& operator<<(std::ostream &out, const test<T> &t);
// rest of class
};
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t) {
// output function defined here
}
Run Code Online (Sandbox Code Playgroud)
首先,我发现一个奇怪的事情是,如果我更改前向声明operator<<以使其不匹配(例如std::ostream& operator<<(std::ostream &out, int fake);,所有内容仍然编译并正常工作(要清楚,我不需要定义这样的函数,但是,如在链接问题中,删除前向声明会导致问题,因为编译器似乎认为我正在声明数据成员而不是友元函数.我很确定这种行为是Visual C++ 2008错误.
有趣的是当我删除前向声明并在上面的代码中使用替代友元声明时.请注意,模板参数U不会出现在以下签名中.此方法也可以正确编译和工作(不更改任何其他内容).我的问题是这是否符合Visual C++ 2008的标准或特性(我在参考书中找不到一个好的答案).
请注意,虽然友元声明template <typename U> friend ... const test<U> &t);也有效,但这实际上为操作员的每个实例提供了friend对任何实例的访问权限test,而我想要的是私有成员test<T>只能从中访问operator<< <T>.我通过实例化test<int>内部operator<<并访问私有成员来测试它; 当我尝试输出a时,这会导致编译错误test<double>.
概要:删除前向声明并切换到上面代码中的替代友元声明似乎产生相同的结果(在Visual C++ 2008中) - 这段代码实际上是否正确?
更新:对代码的任何上述修改在gcc下都不起作用,所以我猜这些是Visual C++编译器中的错误或"功能".我仍然很欣赏熟悉该标准的人的见解.
...如果我改变运算符<<的前向声明,使它不匹配
朋友功能应被视为一种非常特殊的声明.本质上,编译器足以解析声明,但是除非您实际专门化类,否则不会进行语义检查.
在进行建议的修改之后,如果您再实例化test,则会收到有关声明不匹配的错误:
template class test<int>;
Run Code Online (Sandbox Code Playgroud)
......但是......删除前向声明会导致问题
编译器尝试解析声明以存储它,直到类模板专门化.在解析期间,编译器到达<声明中:
friend std::ostream& operator<< <
Run Code Online (Sandbox Code Playgroud)
operator<<可以遵循的唯一方法<是它是否是模板,因此进行查找以检查它是否为模板.如果找到了函数模板,那么它<被认为是模板参数的开始.
删除前向声明时,未找到任何模板,并且该模板operator<<被视为对象.(这也是为什么当你添加using namespace std代码继续编译时,因为必须有模板的声明operator<<).
...当我删除前向声明并在上面的代码中使用替代的朋友声明时.请注意,模板参数U不会出现在以下签名中...
不要求在函数模板的参数中使用所有模板参数.替代声明用于新的函数模板,只有在命名空间中声明并指定显式模板参数时才能调用.
一个简单的例子是:
class A {};
template <typename T> A & operator<<(A &, int);
void foo () {
A a;
operator<< <int> (a, 10);
}
Run Code Online (Sandbox Code Playgroud)
...这段代码实际上是正确的吗?
那么这有两个部分.第一个是替代的友元函数在后面的范围内没有引用声明:
template <typename T>
class test {
template <typename U>
friend std::ostream& operator<<(std::ostream &out, const test<T> &t);
};
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t); // NOT FRIEND!
Run Code Online (Sandbox Code Playgroud)
friend函数实际上将在每个特化的命名空间中声明:
template <typename U>
std::ostream& operator<<(std::ostream &out, const test<int> &t);
template <typename U>
std::ostream& operator<<(std::ostream &out, const test<char> &t);
template <typename U>
std::ostream& operator<<(std::ostream &out, const test<float> &t);
Run Code Online (Sandbox Code Playgroud)
每个特化operator<< <U>都将根据其参数的类型访问特定的特化test<T>.因此,实质上访问受限于您的要求.但是正如我之前提到的,这些函数基本上不能用作运算符,因为你必须使用函数调用语法:
int main ()
{
test<int> t;
operator<< <int> (std << cout, t);
operator<< <float> (std << cout, t);
operator<< <char> (std << cout, t);
}
Run Code Online (Sandbox Code Playgroud)
根据上一个问题的答案,您可以使用litb建议的前向声明,或者根据Dr_Asik的答案定义友元函数内联(这可能就是我要做的).
更新:第二次评论
......在课前改变前方声明; 类中的那个仍然匹配我稍后实现的功能...
正如我在上面指出的那样,编译器operator<<在看到<声明时检查它是否是模板:
friend std::ostream& operator<< <
Run Code Online (Sandbox Code Playgroud)
它通过查找名称并检查它是否是模板来完成此操作.只要您有一个虚拟前向声明,那么这会"欺骗"编译器将您的朋友视为模板名称,因此它<被认为是模板参数列表的开头.
稍后,当您实例化该类时,您确实有一个匹配的有效模板.从本质上讲,您只是欺骗编译器将朋友视为模板专业化.
你可以在这里这样做,因为(正如我之前所说),此时不会进行语义检查.
| 归档时间: |
|
| 查看次数: |
17165 次 |
| 最近记录: |