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

читал несколько вопросов относительно моей проблемы на 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;
}
 starcorn11 янв. 2011 г., 17:55
@Daniel - это не решает проблему, возникающую при перегрузке класса шаблона
 Daniel Trebbien11 янв. 2011 г., 17:52
Был недавний вопрос об этом, который может быть поучительным:stackoverflow.com/questions/4571611/virtual-operator
 David Rodríguez - dribeas11 янв. 2011 г., 20:13
Я думаю, что лучше, если вы не модифицируете вопрос с помощью данного ответа. Это затрудняет определение первоначальной проблемы. Вы можете добавитьРЕДАКТИРОВАТЬ в конце с изменением, котороерешена проблема, но я нахожу это запутанным, когда вопросы со временем меняются, и мне нужно сначала изучить историю, чтобы увидеть, что на самом деле спрашивают.

Ответы на вопрос(5)

Решение Вопроса

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

Экстраверт

Объявите все экземпляры шаблона как друзья. Это то, что вы приняли в качестве ответа, а также то, что предлагает большинство других ответов. При таком подходе вы без необходимости открываете свое конкретное воплощение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...
   }
}
 TheInvisibleFist31 дек. 2014 г., 07:21
намного понятнее, чем выбранный ответ
 dgrat12 мая 2016 г., 19:43
Реализация функции вне класса дает неопределенную ссылку. Внутри класса у меня возникает проблема, как только я явно создаю экземпляр класса хоста с более чем одним типом.
 David Rodríguez - dribeas13 мая 2016 г., 16:36
@dgrat: я не могу отладить код, который не вижу, но он работает. Зависит ли друг от аргументов шаблона? Если это не так, вы получите проблемы с несколькими определениями, так как разные экземпляры будут генерировать именнотакой же функция (или нарушение ODR, если сигнатура функции такая же, но тело отличается).
 WhozCraig15 авг. 2017 г., 21:08
Это продолжает быть одним из моих наиболее рекомендованных для людей, которые ищут друзей шаблонных функций (а не только операторов), в немалой степени из-за явных различий в способах, которыми я могу заниматься, и преимуществ / недостатков для каждой из них. Просто звездный ответ, Дэвид. Я бы поднял это десяток раз, если бы мог.
 isnullxbh02 июл. 2018 г., 21:33
@ DavidRodríguez-dribeas, отлично! Спасибо!

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

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..
}
 Nim11 апр. 2016 г., 10:07
@starcorn, не беспокойтесь, я уже тогда голосовал за ответ Дэвида, это более комплексное решение проблемы ..
 Rupesh Yadav.08 апр. 2016 г., 13:42
@starcorn, вы должны изменить свой выбранный ответ, чтобы обеспечить лучший ответ, и это должен быть ответ Дэвида Родригеса.
 starcorn11 янв. 2011 г., 18:00
спасибо, это работает, отредактировал мой вопрос с этим кодом, я отмечу это как ответ, как только тикер выйдет из строя.
 starcorn08 апр. 2016 г., 13:44
@Nim, основываясь на мнении пользователя, я изменил выбранный ответ на свой вопрос. Однако спасибо за ваш вклад, чтобы помочь мне в то время :)
 David Rodríguez - dribeas11 янв. 2011 г., 20:11
Обратите внимание, что это не объявление конкретного экземпляраoperator<< как друг, а точнеевсе экземпляры, включая любую специализацию шаблона. Смотрите ответВот

Ну вот:

#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;
}
 Gene Bushuyev11 янв. 2011 г., 18:49
Я не думаю, что это должно скомпилировать:template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs), Разработанный тип не допускается при объявлении параметров иtypename Требуется квалифицированный идентификатор.
 John Dibling11 янв. 2011 г., 18:54
@Gene: хм. он компилируется для меня на самых высоких уровнях с отключенными расширениями MS.
 David Rodríguez - dribeas11 янв. 2011 г., 20:08
Он не компилируется с g ++, и я доверяю компилятору в этом. Второй аргумент вoperator<< являетсяclass D<typename classT>и я думаю, что это неправильно. я хотел бы использоватьD<classT> вместо. Ключевое словоclass не является обязательным (99,9% случаев), но использованиеtypename не является одним из двух известных применений: он будет недействительным в качестве заменыclassи это для идентификации того, что зависимое имя в шаблоне на самом деле является типом.

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

#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;
}
 starcorn11 янв. 2011 г., 17:56
Да, я уже сделал это, но что если я не хочуoperator<< как встроенная функция?
 David Rodríguez - dribeas11 янв. 2011 г., 19:50
+1. @starcorn: это решение лучше принятого. Разница невелика, но в принятом ответе вы объявляете все экземпляры (и возможные специализации)operator<< как друзья, в то время как в этом решении вы только предоставляете доступ к созданиюoperator<< который имеет тот же тип. Кроме того, как побочный эффект определенияoperator<< внутри класса вы ограничиваете видимость этогоoperator<< только в тех случаях, когда один из двух аргументов являетсяD - компилятор даже не учитываетoperator<< перегрузка, если только один аргументD<T>.
 Martin York11 янв. 2011 г., 19:33
@starcorn: если метод / функция встроен (неявно или явно), он мало что может сделать, если функция фактически встроена в код. Поэтому это бессмысленное беспокойство.
 David Rodríguez - dribeas11 янв. 2011 г., 19:59
@starcorn: я добавил ответ, который пытается очистить различия в трех разных подходахВот

Вы можете создать открытый вызов метода 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);
}

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

Ваш ответ на вопрос