重载好友运算符<< for class template

问题描述 投票:0回答:5

如果我将重载的

<<
放入内联函数中,它就会起作用。但我该如何让它在我的情况下发挥作用呢?

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

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

编译失败:

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

c++ templates operator-overloading friend ostream
5个回答
210
投票

这是常见问题之一,它们具有相似但并不完全相同的不同方法。这三种方法的不同之处在于您声明谁是您的函数的朋友,以及您如何实现它。

外向的人

将模板的所有实例声明为友元。这是您接受的答案,也是大多数其他答案所建议的。在这种方法中,您不必要地通过声明所有

D<T>
实例化来打开您的特定实例化
operator<<
。也就是说,
std::ostream& operator<<( std::ostream &, const D<int>& )
可以访问
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<int>
可能喜欢将插入运算符应用于自身,但它不希望与
std::ostream& operator<<( std::ostream&, const D<double>& )
有任何关系。

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

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<int>& )
时手动添加:
Test<int>
,以及在使用
Test
或任何其他类型实例化
double
时添加另一个类似的重载。

第三个版本比较麻烦。在不内联代码的情况下,通过使用模板,您可以将模板的单个实例声明为类的友元,而无需向所有其他实例开放:

// 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> ...
}

利用外向者的优势

第三个选项与第一个选项之间的细微差别在于您向其他课程开放的程度。 外向版本中的滥用示例是某人想要访问您的内部并执行以下操作:

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...
   }
}

16
投票

你不能这样声明一个朋友,你需要为它指定一个不同的模板类型。

template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);

注意

SclassT
,这样它就不会遮盖
classT
。定义时

template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
  // body..
}

4
投票

这对我有用,没有任何编译器警告。

#include <iostream>
using namespace std;

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 {
    return (d > rhs.d);
  }

  classT operator=(const D<classT>& rhs);

  friend ostream& operator<< (ostream & os, const D& rhs) {
    os << rhs.d;
    return os;
  }

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

1
投票

我认为你一开始就不应该交朋友。

您可以创建一个公共方法调用 print,如下所示(对于非模板类):

std::ostream& MyClass::print(std::ostream& os) const
{
  os << "Private One" << privateOne_ << endl;
  os << "Private Two" << privateTwo_ << endl;
  os.flush();
  return os;
}

然后,在类之外(但在同一命名空间中)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
  return myClass.print(os);
}

我认为它也应该适用于模板类,但我还没有测试过。


0
投票

给你:

#include <cstdlib>
#include <iostream>
using namespace std;

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 { return d > rhs.d;};
   classT operator=(const D<classT>& rhs);

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

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


int main()
{

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

   cout << my_max(d1,d2) << endl;
   return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.