重载友元运算符<<对于模板类 [英] overloading friend operator&lt;&lt; for template class

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

问题描述

我现在已经在 StackOverflow.com 上阅读了几个关于我的问题的问题,但似乎没有一个能解决我的问题.或者我可能做错了......如果我把它变成内联函数,重载的 << 就可以工作.但是我如何使它在我的情况下起作用?

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong... The overloaded << works if I make it into an inline function. But how do I make it work in my case?

警告:朋友声明 std::ostream&operator<<(std::ostream&, const D<classT>&)'声明一个非模板函数

warning:(如果这不是您想要的,请确保已经声明了函数模板,并在此处的函数名称后添加 <>)-Wno-non-template-friend 禁用此警告

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): 未定义的操作符引用<<(std::basic_ostream >&,D<int>常量&)'collect2: ld 返回 1 个退出状态

代码:

template <class T>
T my_max(T a, T b)
{
   if(a > b)      
      return a;
   else
      return b;
}

template <class classT>
class D
{
public:
   D(classT in)
      : d(in) {};
   bool operator>(const D& rhs) const;
   classT operator=(const D<classT>& rhs);

   friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
   classT d;
};


int main()
{

   int i1 = 1;
   int i2 = 2;
   D<int> d1(i1);
   D<int> d2(i2);

   cout << my_max(d1,d2) << endl;
   return 0;
}

template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
   os << rhs.d;
   return os;
}

推荐答案

这是常见问题之一,这些问题的方法相似但并不完全相同.这三种方法的不同在于您声明谁是您的函数的朋友——然后是您如何实现它.

This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.

性格外向

将模板的所有实例声明为朋友.这是您已接受的答案,也是大多数其他答案的建议.在这种方法中,您通过声明朋友所有 operator<< 实例化,不必要地打开了特定的实例化 D.也就是说,std::ostream&operator<<( std::ostream &, const D& ) 可以访问 D 的所有内部结构.

Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.

template <typename T>
class Test {
   template <typename U>      // all instantiations of this template are my friends
   friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
   // Can access all Test<int>, Test<double>... regardless of what T is
}

内向者

仅将插入运算符的特定实例声明为友元.D 在应用于自身时可能喜欢插入操作符,但它不想与 std::ostream& 有任何关系.运算符<<( std::ostream&, const D& ).

Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).

这可以通过两种方式完成,一种简单的方式是@Emery Berger 提出的,即内联操作符——出于其他原因,这也是一个好主意:

This can be done in two ways, the simple way being as @Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:

template <typename T>
class Test {
   friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
      // can access the enclosing Test. If T is int, it cannot access Test<double>
   }
};

在第一个版本中,您不是创建模板化的operator<<,而是为每个Test 模板.同样,差异很微妙,但这基本上等同于手动添加:std::ostream&operator<<( std::ostream&, const Test& ) 实例化 Test 时,以及实例化 Test<时的另一个类似重载/code> 与 double 或任何其他类型.

In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.

第三个版本比较麻烦.无需内联代码,并使用模板,您可以将模板的单个实例声明为您的类的朋友,而无需向所有 其他实例开放:

The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:

// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );

// Declare the actual templates:
template <typename T>
class Test {
   friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
   // Can only access Test<T> for the same T as is instantiating, that is:
   // if T is int, this template cannot access Test<double>, Test<char> ...
}

利用外向的人

第三个选项与第一个选项之间的细微差别在于您对其他课程的开放程度.extrovert 版本中滥用的一个例子是有人想要进入你的内部并这样做:

The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:

namespace hacker {
   struct unique {}; // Create a new unique type to avoid breaking ODR
   template <> 
   std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
   {
      // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
      // if Test<T> is an introvert, then I can only mess up with Test<unique> 
      // which is just not so much fun...
   }
}

这篇关于重载友元运算符<<对于模板类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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