Overloading Output operator for a class template in a namespace
I've this program
#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> IntTri开发者_StackOverflow社区plet ;
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() ;
}
Compilation fails because compiler could not find any output operator for skg::Triplet. But output operator does exist.
If I move Triplet from skg namespace to global namespace everything works fine. what is wrong here ?
You need to move your implementation of operator<<
into the same namespace as your class. It's looking for:
ostream& operator<< (ostream& os, const skg::Triplet<T>& p_t)
But won't find it because of a short-coming in argument-dependent look-up (ADL). ADL means that when you call a free function, it'll look for that function in the namespaces of it's arguments. This is the same reason we can do:
std::cout << "Hello" << std::endl;
Even though operator<<(std::ostream&, const char*)
is in the std
namespace. For your call, those namespaces are std
and skg
.
It's going to look in both, not find one in skg
(since yours is in the global scope), then look in std
. It will see possibilities (all the normal operator<<
's), but none of those match. Because the code running (the code in ostream_iterator
) is in the namespace std
, access to the global namespace is completely gone.
By placing your operator in the same namespace, ADL works. This is discussed in an article by Herb Sutter: "A Modest Proposal: Fixing ADL.". (PDF). In fact, here's a snippet from the article (demonstrating a shortcoming):
// 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
}
Same situation you have.
The book "C++ Coding Standards" by Sutter and & Alexandrescu has a useful guideline:
- Keep a type and its nonmember function interface in the same namespace.
Follow it and you and ADL will be happy. I recommend this book, and even if you can't get one at least read the PDF I linked above; it contains the relevant information you should need.
Note that after you move the operator, you'll need your friend directive (so you can access private variables):
template <typename U>
friend ostream& operator<< (ostream& os, const Triplet<U>& p_t);
And ta-da! Fixed.
精彩评论