Перегрузка Оператор вывода для шаблона класса в пространстве имен - PullRequest
8 голосов
/ 29 января 2010

У меня есть эта программа

#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std ;

#if 0
namespace skg 
{
 template <class T>
  struct Triplet ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t) ;
#endif

namespace skg
{
 template <class T>
  struct Triplet
  {
 //  friend ostream& ::operator<< <> (ostream& os, const Triplet<T>& p_t) ;

   private:
   T x, y, z ;

   public:
   Triplet (const T& p_x, const T& p_y, const T& p_z)
    : x(p_x), y(p_y), z(p_z) { }
  } ;
}

template <class T>
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)
{
 os << '(' << p_t.x << ',' << p_t.y << ',' << p_t.z << ')' ;
 return os ;
}

namespace {
 void printVector()
 {
  typedef skg::Triplet<int> IntTriplet ;

  vector< IntTriplet > vti ;
  vti.push_back (IntTriplet (1, 2, 3)) ;
  vti.push_back (IntTriplet (5, 5, 66)) ;

  copy (vti.begin(), vti.end(), ostream_iterator<IntTriplet> (cout, "\n")) ;
 }
}
int main (void)
{
 printVector() ;
}

Компиляция не удалась, потому что компилятору не удалось найти оператор вывода для skg :: Triplet. Но оператор вывода существует.

Если я переместлю Triplet из пространства имен skg в глобальное пространство имен, все будет хорошо. что здесь не так?

1 Ответ

13 голосов
/ 29 января 2010

Вам необходимо переместить реализацию operator<< в то же пространство имен, что и ваш класс. Он ищет:

ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)

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

std::cout << "Hello" << std::endl;

Даже если operator<<(std::ostream&, const char*) находится в пространстве имен std. Для вашего звонка эти пространства имен: std и skg.

Это будет смотреть в обоих случаях, а не искать в skg (так как ваш находится в глобальной области видимости), затем искать в std. Он увидит возможности (все нормальные operator<<), но ни одна из них не совпадает. Поскольку выполняющийся код (код в ostream_iterator) находится в пространстве имен std, доступ к глобальному пространству имен полностью закрыт.

Поместив вашего оператора в то же пространство имен, ADL работает. Об этом говорится в статье Херба Саттера: «Скромное предложение: исправление ADL.» . (PDF). Фактически, вот фрагмент из статьи (демонстрирующий недостаток):

// Example 2.4
//
// In some library header:
//
namespace N { class C {}; }
int operator+( int i, N::C ) { return i+1; }

// A mainline to exercise it:
//
#include <numeric>
int main() {
    N::C a[10];
    std::accumulate( a, a+10, 0 ); // legal? not specified by the standard
}

Та же ситуация, что и у вас.

Книга Саттера и Александреску "Стандарты кодирования C ++" содержит полезное руководство:

  1. Храните тип и интерфейс функции, не являющийся членом, в одном и том же пространстве имен.

Следуйте за ним, и вы и ADL будете счастливы. Я рекомендую эту книгу, и даже если вы не можете ее получить, хотя бы прочитайте PDF-файл, который я ссылался выше; он содержит необходимую информацию.


Обратите внимание, что после перемещения оператора вам понадобится директива друга (чтобы вы могли получить доступ к приватным переменным):

template <typename U>
friend ostream& operator<< (ostream& os, const Triplet<U>& p_t);

И та-да! Исправлено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...