Перегрузка оператора вывода для шаблона класса в пространстве имен
У меня есть эта программа
#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. Но оператор вывода существует.
Если я перемещаю Триплет из пространства имен skg в глобальное пространство имен, все работает нормально. что здесь не так ?
1 ответов
вам нужно переместить реализацию 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<<
' s), но ни один из них не соответствует. потому что код работает (код ostream_iterator
) находится в пространстве имен std
доступ к глобальному пространству имен полностью исчезли.
разместив оператора в том же пространстве имен, ADL работает. Это обсуждается в статье Herb Саттер:"скромное предложение: исправление 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++" Саттер и & Александреску имеет полезную директиву:
- держите тип и его интерфейс функции, не являющийся членом, в одном пространстве имен.
следуйте за ним и вы с АДЛОМ будете счастливы. Я рекомендую эту книгу, и даже если вы не можете получить хотя бы один, прочитайте PDF, который я связал выше; он содержит соответствующую информацию, которая вам понадобится.
обратите внимание, что после перемещения оператора вам понадобится директива friend (чтобы вы могли получить доступ к частным переменным):
template <typename U>
friend ostream& operator<< (ostream& os, const Triplet<U>& p_t);
и та-да! Зафиксированный.