Как вы объявляете друга универсального класса вне описания класса? - PullRequest
3 голосов
/ 18 марта 2012

Следующий код работает, но я хотел бы переместить ostream & operator << за пределы объявления класса, как у меня с hash :: operator []. </p>

#include<iostream>
#include<map>

using namespace std;

template <class T>
class hash {
  private:
    map<string, T> _map;
  public:
    T& operator[] (string x);
    friend ostream& operator<<(ostream& out, const hash<T> &rhs) { return out << "test"; }
};

template <class T>
T & hash<T>::operator[](string x) {
  return _map[x];
}

int main () {
  hash<int> myobject;
  myobject["a"] = 1;
  cout << myobject["a"] << endl;
  cout << myobject << endl;
  return 0;
}

Я пробовал:

template <class T>
ostream& operator<<(...) {
  return out << "test";
}

и

ostream& operator<<(...) {
  return out << "test";
}

, а также несколько других комбинаций безрезультатно.

Ответы [ 2 ]

6 голосов
/ 18 марта 2012

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

template <typename T>
class test {
   int private_field;
   friend std::ostream& operator<<( std::ostream&, test<T> const & );
};
// ???
int main() {
   std::cout << test<int>() << std::endl;
}

Шаблоны создаются по требованию (если вы не создали их явно), это означает, что в данной конкретной программе test создается только как test<int>. Когда компилятор создает экземпляр шаблона (поскольку он был запрошен в main), он обрабатывает определение шаблона. На этом этапе поведение аналогично переписыванию кода с замещенным типом в точке определения (в данном случае прямо перед main):

class test<int> {
   friend std::ostream& operator<<( std::ostream&, test<int> const & );
};

Теперь, если вы посмотрите на созданный экземпляр шаблона, вы можете заметить, что объявление friend оказывает поддержку не шаблонной функции. Так что в этой конкретной программе вы можете заполнить ??? этой конкретной функцией:

std::ostream& operator<<( std::ostream& o, test<int> const & t ) {
   return o << t.private_field;
}

Проблема здесь в том, что это нелегко расширить. Код этого operator<< такой же для test<int>, что и для test<double>, поэтому не нужно переписывать одинаковую функцию для всех экземпляров всех типов!

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

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

Первый случай:

template <typename T>
class test {
   template <typename U>
   friend std::ostream& operator<<( std::ostream&, test<U> const & );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

Во втором случае требуется пара предварительных объявлений:

template <typename T> test;
template <typename T> std::ostream& operator<<( std::ostream&, test<T> const & );
template <typename T>
class test {
   friend std::ostream& operator<< <T>( std::ostream&, const test<T>& );
};
template <typename T>
std::ostream& operator<<( std::ostream&, test<T> const & ) { ... }

Есть, конечно, другой вариант: вообще не объявлять friend s. Предоставьте print(std::ostream&) открытую функцию в вашем классе, которая имеет реализацию, и предоставьте не шаблонную operator<<, которая просто вызывает print для второго аргумента.

3 голосов
/ 18 марта 2012

Вы должны быть осторожны при использовании друга и шаблона

#include<iostream>
#include<map>

template <class T>
class MyHash{
public:
    // ... use your T template here
    template <class U>
    friend ostream& operator<<(ostream& out, const MyHash<U> &rhs);
};

// ...
template <class U>
ostream& operator<<(ostream& out, const MyHash<U> &rhs){ return out << "test"; }

вам нужно использовать другой шаблон U , потому что operator << </strong> - это метод friend (внешний), если мы используем T вместо U у нас будет: неоднозначное определение в шаблоне .

Измените хэш на MyHash , чтобы избежать неясностей.

Редактировать:

Ошибка, которую вы получаете здесь :

ошибка C2676: '<' binaire: 'std :: string' ne définit pas cet opérateur Вы можете конвертировать все типы, приемлемые для начинающих </p>

потому что вы забыли включить <string> в "hash.h" . Этот заголовок определяет оператор <</strong>.

А также попробуйте переместить определение операторов [] и operator << </strong> непосредственно в "hash.h" И объявление, и определение шаблонов должны быть включены. Это связано с тем, что в некоторых компиляторах шаблонные функции не могут быть скомпилированы и связаны независимо, поскольку они генерируются по запросу для конкретных типов, с которыми они создаются.

Измените # include "hash.cpp" на # include "hash.h"

...