0

I'm trying to overload the << operator for a class template, but the compiler gives me a linker error. The goal is to be able to send a de-referenced base class pointer to std::cout so that the derived operator<< gets called.

Is this possible?

class IBase
{
public:
    IBase() {};
    virtual ~IBase() {};
};

template <typename T>
class Derived
    : public IBase
{
public:
    Derived(T data);
    friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);
private:
    T data_;
};

template <typename T>
Derived<T>::Derived(T data)
    : IBase(),
      data_(data)
{
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const Derived<T>& dt)
{
    os << dt.data_;
    return os;
}


int _tmain(int argc, _TCHAR* argv[])
{


    // Question 1
    Derived<int> der(234);
    std::cout << der;

    // Question 2
    //IBase* base = new Derived<int>(5);
    // std::cout << *base
}

Here are the errors:

error LNK2001: unresolved external symbol "class std::basic_ostream > & __cdecl operator<<(class std::basic_ostream

&,class Derived const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$Derived@H@@@Z)

and

fatal error LNK1120: 1 unresolved externals

Q-bertsuit
  • 3,223
  • 6
  • 30
  • 55

5 Answers5

2

You need to declarate friend operator as a template too

template<typename T1>
friend std::ostream& operator<<(std::ostream& os, const Derived<T1>& dt);
Anton Lashkov
  • 280
  • 3
  • 10
2

friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);

declares a non template version friend, so you would have to implement

std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);

for every T you use:

std::ostream& operator<<(std::ostream& os, const Derived<int>& dt) {
    return os << dt.data_;
}

And so on. A way to do it without duplicate code is with definition inside the class:

template <typename T>
class Derived : public IBase
{
public:
    explicit Derived(T data);
    friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt)
    {
        return os << dt.data_;
    }
private:
    T data_;
};

Demo

An other alternative is to make the function template.

// Forward declarations
template <typename T> class Derived;
template <typename T> std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);

And then in Derived, you have 2 choices:

  • Declare each the template method friend:

    template <typename T2>
    friend std::ostream& operator<<(std::ostream& os, const Derived<T2>& dt);
    

    So std::ostream& operator<<(std::ostream& os, const Derived<int>& dt) has access to private members of Derived<int>, but also to the ones of Derived<char>

    Demo

  • Declare only the template which matches the argument:

    friend std::ostream& operator<< <>(std::ostream& os, const Derived& dt);
    

    and so std::ostream& operator<<(std::ostream& os, const Derived<int>& dt) doesn't have access to private members of Derived<char>.

    Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

replace

friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);

with

template<typename D>
friend std::ostream& operator<<(std::ostream& os, const Derived<D>& dt);
stas.yaranov
  • 1,797
  • 10
  • 17
0

Your immediate problem is that the operator needs to be templated.

However, your ultimate goal can't be attained through templates alone as dereferencing a IBase* gets you a IBase& - template instantiation occurs at compile time and the compiler has no access to the runtime type.

So your templated operator will never be used if you pass a dereferenced IBase* to operator <<.

Instead, add a virtual output function to the base and override it:

class IBase
{
public:
    IBase() {};
    virtual ~IBase() {};
    virtual std::ostream& output(std::ostream&) const = 0;
};

template <typename T>
class Derived
    : public IBase
{
public:
    Derived(T data);
    virtual std::ostream& output(std::ostream& os) const
    {
        os << data;
        return os;
    }
private:
    T data_;
};

std::ostream& operator<<(std::ostream& os, const IBase& dt)
{
    return dt.output(os);
}
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • Thank you for this! I asked this as a separate question a few minutes ago, please post your answer there so that I can accept it. http://stackoverflow.com/questions/33496137/virtual-operator-and-templates – Q-bertsuit Nov 03 '15 at 10:02
0

You don't need to make your friend a template:

friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);

This needs to be changed to:

friend std::ostream& operator<< <>(std::ostream& os, const Derived<T>& dt);

The operator<< function will need to be visible before the friend declaration for this form to work.

You should prefer this form to making the friend a template because you don't need all of the operator's instantiations to be friends, just one of them.

Simple
  • 13,992
  • 2
  • 47
  • 47