2

I am trying to overload the ostream operator in template and inherited classes and I have been following some tips here and here, but I get a redefinition error. Here is a reproduction of my code:

#include <iostream>

enum type
{
        A,
        B
};

template <type T>
class base
{
protected:
        virtual std::ostream& print(std::ostream& out) const =0;
};

template <type T>
class derived: public base<T>
{
protected:
        virtual std::ostream& print(std::ostream& out) const
        {
                out<<"Hello World.\n";
                return out;
        }
public:
        template <type S>
        friend std::ostream& operator<<(std::ostream& out, const derived<S>& D)
        {
                return (D.print(out));
        }
};

int main ()
{
#ifdef __NOT_WORKING__
        derived<A> a;
        std::cout<<a;
        derived<B> b;
        std::cout<<b;
#else
        derived<A> a;
        std::cout<<a;
#endif
        return 0;
}

If I define just a derived A class, everything works, but if I define a derived A and a derived B class I get this error from the compiler:

test.cpp: In instantiation of 'class derived<(type)1u>':
test.cpp:38:20:   required from here
test.cpp:27:30: error: redefinition of 'template<type S> std::ostream& operator<<(std::ostream&, const derived<S>&)'
         friend std::ostream& operator<<(std::ostream& out, const derived<S>& D)
                              ^
test.cpp:27:30: note: 'template<type S> std::ostream& operator<<(std::ostream&, const derived<S>&)' previously defined here
test.cpp: In instantiation of 'std::ostream& operator<<(std::ostream&, const derived<S>&) [with type S = (type)1u; type T = (type)0u; std::ostream = std::basic_ostream<char>]':
test.cpp:39:20:   required from here
test.cpp:20:31: error: 'std::ostream& derived<T>::print(std::ostream&) const [with type T = (type)1u; std::ostream = std::basic_ostream<char>]' is protected
         virtual std::ostream& print(std::ostream& out) const
                               ^
test.cpp:29:37: error: within this context
                 return (D.print(out));
                                     ^

Why is it redefining the friend function? Thanks for your time.

PS. I am using gcc49.

Community
  • 1
  • 1
giogio12345
  • 115
  • 6

2 Answers2

2

Replace

template <type S>
friend std::ostream& operator<<(std::ostream& out, const derived<S>& D)
{
    return (D.print(out));
}

with

friend std::ostream& operator<<(std::ostream& out, const derived<T>& D)
{
    return (D.print(out));
}

error will go away.

With earlier definition, you are trying to define a new template function with same signature.

user1
  • 4,031
  • 8
  • 37
  • 66
0

[temp.friend]/4:

When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used. The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.

Clang compiles the above fine - that can be considered a bug, but recall that violations of the ODR are ill-formed with no diagnostic required.


You have two ways to solve this. Extract the definition of the template:

template <typename T>
class derived: public base<T>
{
//     [..]

       template <type S>
       friend std::ostream& operator<<(std::ostream& out, const derived<S>& D);
};

template <type S>
std::ostream& operator<<(std::ostream& out, const derived<S>& D)
{
    return (D.print(out));
}

Or make the operator a non-template:

template <type T>
class derived: public base<T>
{
//     [..]

       friend std::ostream& operator<<(std::ostream& out, const derived<T>& D)
       {
               return (D.print(out));
       }
};
Columbo
  • 60,038
  • 8
  • 155
  • 203