Reputation: 658
The following example compiles fine but I can't figure out how to separate declaration and definition of operator<<() is this particular case.
Every time I try to split the definition friend is causing trouble and gcc complains the operator<<() definition must take exactly one argument.
#include <iostream>
template <typename T>
class Test {
public:
Test(const T& value) : value_(value) {}
template <typename STREAM>
friend STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
os << rhs.value_;
return os;
}
private:
T value_;
};
int main() {
std::cout << Test<int>(5) << std::endl;
}
Operator<<() is supposed to have a free first parameter to work with different kind of output streams (std::cout, std::wcout or boost::asio::ip::tcp::iostream). The second parameter should be bound to a specialized version of the surrounding class.
Test<int> x;
some_other_class y;
std::cout << x; // works
boost::asio::ip::tcp::iostream << x; // works
std::cout << y; // doesn't work
boost::asio::ip::tcp::iostream << y; // works
Besides that using a non-member-function isn't equivalent to splitting the definition and declaration because non-member-functions can't access private attributes the the class.
Upvotes: 5
Views: 1049
Reputation: 658
For each instantiated type T of class Test a template function operator<<() is exposed which can operate on different kind of streams. The operator<<() function has a free first parameter but a fixed second parameter.
example:
Test<int> x;
some_other_class y;
std::cout << x; // works
boost::asio::ip::tcp::iostream << x; // works
std::cout << y; // doesn't work
boost::asio::ip::tcp::iostream << y; // works
That's the way the Test class was supposed to work.
Upvotes: 0
Reputation: 208353
The problem is that in the code that you present the friend is a templated function parametrized only on the first argument type. That is, for each instantiating type T of the class template (call it mytype
), you are declaring a free template function:
template <typename STREAM>
STREAM& operator<<( STREAM& os, Test<mytype> const & x );
The important point there is that Test<mytype>
is the particular instantiation of Test
with type argument mytype
.
If you really want to declare a friend function that is templated in both the stream type and the instantiating type of the Test
template, you must declare a friend with two arguments.
On the other hand, I recommend that you do not parametrize operator<<
on the stream type, and at the same time, that you define it inside the class braces as it has slight advantages (name lookup rules are slightly different).
Upvotes: 0
Reputation: 37364
Shouldn't it be defined outside of the class ?
template <typename T>
class Test
{
...
template <typename STREAM>
friend STREAM& operator<<(STREAM& os, const Test<T>& rhs);
};
template <typename STREAM, typename T>
STREAM& operator<<(STREAM& os, const Test<T>& rhs)
{
os << rhs.value_;
return os;
}
Upvotes: 1
Reputation: 52294
The nearest I can achieve is
#include <iostream>
template <typename T>
class Test;
template <typename STREAM, typename T>
STREAM& operator<<(STREAM& os, const Test<T>& rhs);
template <typename T>
class Test {
public:
Test(const T& value) : value_(value) {}
template <typename STREAM, typename U>
friend STREAM& operator<< (STREAM& os, const Test<U>& rhs);
private:
T value_;
};
template <typename STREAM, typename T>
STREAM& operator<<(STREAM& os, const Test<T>& rhs) {
os << rhs.value_;
return os;
}
int main() {
std::cout << Test<int>(5) << std::endl;
}
which declares all operator<< as friend instead of only the one parametrized by T. The problem is that it isn't possible to partially specialize functions. One would have liked to use
template <typename STREAM>
friend STREAM& operator<< <STREAM, T> (STREAM& os, const Test<T>& rhs);
but that isn't valid syntax. (Well, and partial specialization can't declared friend)
Upvotes: 1
Reputation: 84169
The easiest is probably to make all these template operators friends:
#include <iostream>
template <typename T>
class Test
{
public:
Test(const T& value) : value_(value) {}
template <typename STREAM, typename U>
friend STREAM& operator<<(STREAM& os, const Test<U>& rhs);
private:
T value_;
};
template <typename STREAM, typename T>
STREAM& operator<<( STREAM& os, const Test<T>& rhs )
{
os << rhs.value_;
return os;
}
Upvotes: 4