gpt4 book ai didi

c++ - 模板类的模板友元函数

转载 作者:可可西里 更新时间:2023-11-01 16:39:05 30 4
gpt4 key购买 nike

我一直在努力解决 this question 中描述的问题(将模板函数声明为模板类的 friend ),我相信第二个答案是我想要做的(转发声明模板函数,然后将特化命名为 friend )。我有一个问题,即稍微不同的解决方案实际上是正确的还是恰好适用于 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
}

首先,我发现一件奇怪的事情是,如果我更改 operator<< 的前向声明所以它不匹配(例如 std::ostream& operator<<(std::ostream &out, int fake); ,一切仍然编译并正常工作(要清楚,我不需要定义这样的函数,只需声明它)。但是,如链接到问题,删除前向声明会导致问题,因为编译器似乎认为我正在声明数据成员而不是友元函数。我很确定此行为是 Visual C++ 2008 错误。

有趣的是,当我删除前向声明并在上面的代码中使用替代的友元声明时。注意模板参数 U没有出现在以下签名中。此方法也可以正确编译和工作(无需更改任何其他内容)。我的问题是这是否符合标准或 Visual C++ 2008 的特性(我在我的引用书中找不到好的答案)。

注意当 friend 声明template <typename U> friend ... const test<U> &t);也有效,这实际上给出了运算符的每个实例 friend访问 test 的任何实例,而我想要的是 test<T> 的私有(private)成员应该只能从 operator<< <T> 访问.我通过实例化 test<int> 来测试它在operator<<里面并访问私有(private)成员;当我尝试输出 test<double> 时,这应该会导致编译错误.

概要:在上面的代码中删除前向声明并切换到替代友元声明似乎产生相同的结果(在 Visual C++ 2008 中)——这段代码真的正确吗?

更新:上述任何对代码的修改在 gcc 下都不起作用,所以我猜测这些是 Visual C++ 编译器中的错误或“功能”。不过,我还是很感谢熟悉该标准的人提供的见解。

最佳答案

...如果我更改 operator<< 的前向声明,使其不匹配

友元函数应该被看作是一种非常特殊的声明类型。本质上,编译器会做足够的工作来解析声明,但是除非您真正特化该类,否则不会进行语义检查。

进行建议的修改后,如果您随后实例化 test您将收到有关声明不匹配的错误消息:

template class test<int>;

...但是...删除前向声明会导致问题

编译器会尝试解析声明并存储它,直到类模板被特化为止。在解析期间,编译器到达 <在声明中:

friend std::ostream& operator<<  <

operator<< 的唯一途径后面可以跟< is 如果它是一个模板,那么会进行查找以检查它是否是一个模板。如果找到函数模板,则 <被认为是模板参数的开始。

当您删除前向声明时,找不到模板并且 operator<<被认为是一个对象。 (这也是为什么当您添加 using namespace std 时代码会继续编译,因为必须有 operator<< 的模板声明)。

...当我删除前向声明并在上面的代码中使用替代的友元声明时。请注意,模板参数 U 未出现在以下签名中...

不要求所有模板参数都用于函数模板的参数中。替代声明用于新函数模板,只有在命名空间中声明并指定显式模板参数时才可调用。

一个简单的例子是:

class A {};
template <typename T> A & operator<<(A &, int);

void foo () {
A a;
operator<< <int> (a, 10);
}

...这段代码真的正确吗?..

好吧,这有两个部分。首先是备选友元函数不引用作用域后面的声明:

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!

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);

operator<< <U>的每个特化将根据其参数的类型访问特定的特化 test<T> .所以本质上,访问是根据您的需要受到限制的。然而,正如我之前提到的,这些函数基本上不能用作运算符,因为您必须使用函数调用语法:

int main ()
{
test<int> t;
operator<< <int> (std << cout, t);
operator<< <float> (std << cout, t);
operator<< <char> (std << cout, t);
}

根据上一个问题的答案,您可以使用 litb 建议的前向声明, 或者你按照 Dr_Asik's 定义 friend 函数内联回答(这可能是我会做的)。

更新:第二条评论

...改变课前的前置声明;类的还是符合我后面实现的功能...

正如我上面所指出的,编译器检查是否 operator<<当它看到 < 时是一个模板在声明中:

friend std::ostream& operator<<  <

它通过查找名称并检查它是否是模板来完成此操作。只要你有一个虚拟的前向声明,那么这就会“欺骗”编译器将你的 friend 视为模板名称,因此 <被认为是模板参数列表的开始。

稍后,当您实例化该类时,您确实有一个有效的模板可以匹配。本质上,您只是在欺骗编译器将友元视为模板特化。

您可以在此处执行此操作,因为(正如我之前所说)此时没有进行语义检查。

关于c++ - 模板类的模板友元函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1787143/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com