перегрузка друга оператора друга << для класса шаблонов

StackOverflow https://stackoverflow.com/questions/4660123

Вопрос

Теперь я прочитал пару вопросов, касающихся моей проблемы на Stackoverflow.com, и ничто из этого не решает мою проблему. Или я, возможно, сделал это неправильно ... перегружено << Работает, если я сделаю это в встроенную функцию. Но как мне заставить это работать в моем случае?

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

Код:

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;
}
Это было полезно?

Решение

Это один из тех часто задаваемых вопросов, которые имеют разные подходы, которые похожи, но не совсем одинаковы. Три подхода отличаются по тем, кем вы объявляете, что он является другом вашей функции -и затем, как вы ее реализуете.

Экстраверт

Объявите все экземпляры шаблона как друзья. Это то, что вы приняли в качестве ответа, а также то, что предлагают большинство других ответов. При таком подходе вы без необходимости открывать свой конкретный экземпляр 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...
   }
}

Другие советы

Вы не можете объявить такого друга, вам нужно указать другой тип шаблона для него.

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

Это сработало для меня без каких -либо предупреждений компилятора.

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

Ну вот:

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

Я думаю, что вы не должны подружиться в первую очередь.

Вы можете создать публичный метод Печать, что -то вроде этого (для класса без шаблонов):

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

Я думаю, что это должно работать также для класса шаблонов, но я еще не тестировал.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top