Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overloading friend operator<< for template class

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong... The overloaded << works if I make it into an inline function. But how do I make it work in my case?

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function

warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)' collect2: ld returned 1 exit status

The code:

template <class T> T my_max(T a, T b) {    if(a > b)             return a;    else       return b; }  template <class classT> class D { public:    D(classT in)       : d(in) {};    bool operator>(const D& rhs) const;    classT operator=(const D<classT>& rhs);     friend ostream& operator<< (ostream & os, const D<classT>& rhs); private:    classT d; };   int main() {     int i1 = 1;    int i2 = 2;    D<int> d1(i1);    D<int> d2(i2);     cout << my_max(d1,d2) << endl;    return 0; }  template <class classT> ostream& operator<<(ostream &os, const D<classT>& rhs) {    os << rhs.d;    return os; } 
like image 452
starcorn Avatar asked Jan 11 '11 16:01

starcorn


People also ask

Can << operator be overloaded?

Output streams use the insertion ( << ) operator for standard types. You can also overload the << operator for your own classes.

Why must << be overloaded as a friend function?

Why must << be overloaded as a friend function? It is not necessary. It is usually done because this function often accesses the private and protected members of the object it displays. overloaded operator<< or operator>> function.

What is operator overloading explain the >> << operator with the help of example?

This means C++ has the ability to provide the operators with a special meaning for a data type, this ability is known as operator overloading. For example, we can overload an operator '+' in a class like String so that we can concatenate two strings by just using +.

Is overloading is possible for template functions?

A template function can be overloaded either by a non-template function or using an ordinary function template.


2 Answers

This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.

The extrovert

Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.

template <typename T> class Test {    template <typename U>      // all instantiations of this template are my friends    friend std::ostream& operator<<( std::ostream&, const Test<U>& ); }; template <typename T> std::ostream& operator<<( std::ostream& o, const Test<T>& ) {    // Can access all Test<int>, Test<double>... regardless of what T is } 

The introverts

Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).

This can be done in two ways, the simple way being as @Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:

template <typename T> class Test {    friend std::ostream& operator<<( std::ostream& o, const Test& t ) {       // can access the enclosing Test. If T is int, it cannot access Test<double>    } }; 

In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.

The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:

// Forward declare both templates: template <typename T> class Test; template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );  // Declare the actual templates: template <typename T> class Test {    friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& ); }; // Implement the operator template <typename T> std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {    // Can only access Test<T> for the same T as is instantiating, that is:    // if T is int, this template cannot access Test<double>, Test<char> ... } 

Taking advantage of the extrovert

The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:

namespace hacker {    struct unique {}; // Create a new unique type to avoid breaking ODR    template <>     std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )    {       // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!       // if Test<T> is an introvert, then I can only mess up with Test<unique>        // which is just not so much fun...    } } 
like image 134
David Rodríguez - dribeas Avatar answered Nov 05 '22 02:11

David Rodríguez - dribeas


You can't declare a friend like that, you need to specify a different template type for it.

template <typename SclassT> friend ostream& operator<< (ostream & os, const D<SclassT>& rhs); 

note SclassT so that it doesn't shadow classT. When defining

template <typename SclassT> ostream& operator<< (ostream & os, const D<SclassT>& rhs) {   // body.. } 
like image 42
Nim Avatar answered Nov 05 '22 01:11

Nim