模板类的模板朋友功能 [英] Template friend function of a template class

查看:73
本文介绍了模板类的模板朋友功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为此问题(声明一个模板函数作为模板类的朋友),我相信第二个答案就是我想做的事(向前声明模板函数,然后将一个专业名称命名为朋友).我有一个疑问,就是稍微不同的解决方案实际上是正确的,还是恰好在Visual C ++ 2008中起作用.

I was struggling with the issue described in this question (declaring a template function as a friend of a template class), and I believe the 2nd answer is what I want to do (forward declare the template function, then name a specialization as a friend). I have a question about whether a slightly different solution is actually correct or just happens to work in 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错误.

First, one strange thing I found was that if I change the forward declaration of operator<< so that it doesn't match (for example, std::ostream& operator<<(std::ostream &out, int fake);, everything still compiles and works correctly (to be clear, I don't need to define such a function, only declare it). However, as in the linked-to question, removing the forward declaration causes a problem as the compiler seems to think I'm declaring a data member instead of a friend function. I'm pretty sure that this behaviour is a Visual C++ 2008 bug.

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

The interesting thing is when I remove the forward declarations and use the alternative friend declaration in the code above. Note that the template parameter U doesn't appear in the following signature. This method also compiles and works correctly (without changing anything else). My question is whether this is conforming to the standard or an idiosyncrasy of Visual C++ 2008 (I couldn't find a good answer in my reference books).

请注意,虽然朋友声明template <typename U> friend ... const test<U> &t);也可以使用,但这实际上使操作员friend的每个实例都可以访问test的任何实例,而我想要的是test<T>的私有成员应仅可从operator<< <T>访问.我通过实例化operator<<中的test<int>并访问一个私有成员来进行测试.当我尝试输出test<double>时,这应该会导致编译错误.

Note that while a friend declaration template <typename U> friend ... const test<U> &t); also works, this actually gives each instance of the operator friend access to any instance of test, while what I want is that the private members of test<T> should only be accessible from operator<< <T>. I tested this by instantiating a test<int> inside the operator<< and accessing a private member; this should cause a compile error when I try to output a test<double>.

简介:在上面的代码中删除前向声明并切换到替代朋友声明似乎会产生相同的结果(在Visual C ++ 2008中)–该代码实际上正确吗?

Synopsis: Removing the forward declarations and switching to the alternative friend declaration in the code above seems to produce the same result (in Visual C++ 2008) -- is this code actually correct?

更新:以上对代码的任何修改在gcc下均不起作用,因此我猜测这些是Visual C ++编译器中的错误或功能".尽管如此,我还是很感谢熟悉该标准的人们的见识.

UPDATE: Any of the above modifications to the code don't work under gcc, so I'm guessing that these are errors or "features" in the Visual C++ compiler. Still I'd appreciate insights from people familiar with the standard.

推荐答案

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

朋友功能应被视为一种非常特殊的声明类型.从本质上讲,编译器足以解析声明,但是除非您真正专门化该类,否则不会进行语义检查.

A friend function should be seen as a very special type of declaration. In essence, the compiler does enough to parse the declaration however no semantic checking will take place unless you actually specialize the class.

进行建议的修改后,如果实例化test,则会收到有关声明不匹配的错误:

After making your suggested modification, if you then instantiate test you will get an error about the declarations not matching:

template class test<int>;

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

编译器将尝试解析该声明以将其存储,直到专门化类模板为止.在解析期间,编译器到达声明中的<:

The compiler tries to parse the declaration to store it until the class template is specialized. During the parse, the compiler reaches the < in the declaration:

friend std::ostream& operator<<  <

operator<<后跟<的唯一方法是是否为模板,因此将进行查找以检查它是否为模板.如果找到了功能模板,则将<视为模板参数的开始.

The only way that operator<< could be followed by < is if it is a template, so a lookup takes place to check that it is a template. If a function template is found, then the < is considered to be the start of template arguments.

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

When you remove the forward declaration, no template is found and operator<< is considered to be an object. (This is also why when you add using namespace std the code continues to compile as there must be declarations of templates for operator<<).

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

不要求在功能模板的参数中使用所有模板参数.替代声明是针对新功能模板的,只有在名称空间中声明并指定了显式模板参数时,该函数模板才可以调用.

There is no requirement that all template parameters be used in the arguments of a function template. The alternative declaration is for a new function template that will only be callable if declared in the namespace and specifying explicit template arguments.

一个简单的例子是:

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

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

...此代码实际上正确吗?..

这有两部分.首先是替代的friend函数不会在范围的后面引用该声明:

Well there are two parts to this. The first is that the alternative friend function does not refer to the declaration later in the scope:

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!

该朋友函数实际上将在每种特殊化的名称空间中声明:

The friend function would actually be declared in the namespace for each specialization:

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>的类型访问特定的特殊化.因此,实质上,访问是根据您的要求进行限制的.但是,正如我之前提到的那样,这些函数基本上不能用作运算符,因为必须使用函数调用语法:

Every specialization of operator<< <U> will have access to the specific specialization as per the type of its parameter test<T>. So in essence the access is restricted as you require. However as I mentioned before these functions are basically unusable as operators, since you must use function call syntax:

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

根据上一个问题的答案,您可以使用 Dr_Asik的答案(可能是我会做的).

As per the answers to the previous question, you either use the forward declaration as suggested by litb, or you go with defining the friend function inline as per Dr_Asik's answer (which would probably be what I would do).

更新:第二条评论

...在课程开始前更改前向声明;该类中的一个仍然与我稍后实现的功能相匹配...

如上所述,编译器在声明中看到<时会检查operator<<是否为模板:

As I pointed out above, the compiler checks if operator<< is a template when it sees the < in the declaration:

friend std::ostream& operator<<  <

它通过查找名称并检查它是否是模板来实现.只要您有一个虚假的前向声明,然后这就会欺骗"编译器将您的朋友当作模板名称,因此<被认为是模板参数列表的开始.

It does this by looking up the name and checking if it is a template. As long as you have a dummy forward declaration then this "tricks" the compiler into treating your friend as a template name and so the < is considered to be the start of a template argument list.

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

Later, when you instantiate the class, you do have a valid template to match. In essence, you're just tricking the compiler into treating the friend as a template specialization.

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

You can do this here because (as I said earlier), no semantic checking takes place at this point in time.

这篇关于模板类的模板朋友功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆