1

I'm trying to implement a sort of virtual operator << that lets me send a IBase class object to cout so that it calls the Derived class' operator <<. Is this possible?

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

template <typename T>
class Derived
    : public IBase
{
public:
    Derived(T data);
    template <typename U>
    friend std::ostream& operator<<(std::ostream& os, const Derived<U>& 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[])
{
    IBase* base = new Derived<int>(5);

    std::cout << *base;
}
Q-bertsuit
  • 3,223
  • 6
  • 30
  • 55

3 Answers3

0

The << operator is a function, not a method and thus it can not be virtual. However you can still achieve what you want- remember that the operator<< takes a reference as parameter? Well polymorphism works on those too. Simply add another virtual method that you call from the << operator.

Have a look at this short example I put together:

#include <iostream>
#include <string>
using namespace std;

class Base {
 public:
 virtual string toString() const {
   return "base";
 }
};

class Child : public Base {
 public:
  virtual string toString()  const {
   return "child";
  }

  friend ostream& operator<<(ostream& out, const Base& b);
};

ostream& operator<<(ostream& out, const Base& b) {
    out << b.toString();
    return out;
}

int main() {
    Child c;
    cout << c;
    return 0;
}

And here is a link in ideone: http://ideone.com/EmP1oP

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
0

Your 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.

(Dynamic dispatch only happens when you invoke a member function of an object, and a binary operator can't be a member of its right-hand operand.)

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
0

You can make it virtual for real with a help of simple template rig (which, in addition, does not prevent de-virtualization).

struct X {
    virtual std::ostream& repr(std::ostream& out) const;
}

template <class X>
std::enable_if_t<
    std::is_same<
        std::void_t<
            decltype(std::declval<X>().repr(std::declval<std::ostream>()))>,
            void>::value,
    std::ostream&>
operator<<(X const& x)
{
    return x.repr(out);
}
bobah
  • 18,364
  • 2
  • 37
  • 70