Подтвердить что ты не робот

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

Я прочитал пару вопросов относительно моей проблемы на 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;
}
4b9b3361

Ответ 1

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

Экстраверт

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

Здесь вы идете:

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

Ответ 5

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

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

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

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

Ответ 6

Это хорошая статья: C++ Классы шаблонов и подробности о функциях друзей https://web.mst.edu/~nmjxv3/articles/templates.html В нем также упоминается ответ Дэвида Родригеса-Дрибеаса.