51

I need to use a virtual << operator. However, when I try to write:

virtual friend ostream & operator<<(ostream& os,const Advertising& add);

I get the compiler error

Error 1 error C2575: 'operator <<' : only member functions and bases can be virtual

How can I turn this operator virtual?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
inna karpasas
  • 601
  • 2
  • 7
  • 6

3 Answers3

100

The problem with this setup is that the operator<< you defined above is a free function, which can't be virtual (it has no receiver object). In order to make the function virtual, it must be defined as a member of some class, which is problematic here because if you define operator<< as a member of a class then the operands will be in the wrong order:

class MyClass {
public:
    virtual ostream& operator<< (ostream& out) const;
};

means that

MyClass myObject;
cout << myObject;

will not compile, but

MyClass myObject;
myObject << cout;

will be legal.

To fix this, you can apply the Fundamental Theorem of Software Engineering - any problem can be solved by adding another layer of indirection. Rather than making operator<< virtual, consider adding a new virtual function to the class that looks like this:

class MyClass {
public:
    virtual void print(ostream& where) const;
};

Then, define operator<< as

ostream& operator<< (ostream& out, const MyClass& mc) {
    mc.print(out);
    return out;
}

This way, the operator<< free function has the right parameter order, but the behavior of operator<< can be customized in subclasses.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • thanks it is very helpful. i thought a bout this solution,but i thought maybe There is another way that I do not know that easier for implementation. – inna karpasas Dec 31 '10 at 18:23
  • 7
    *"any problem can be solved by adding another layer of indirection"* - remember, any problem except the problem of too many layers of indirection ;) – Kos Dec 31 '10 at 18:43
  • 18
    @Kos: No, no. As long as you've declared it `unsigned indirection;`, you just have to keep adding more and more indirection and the problem will solve itself when you roll over – James McNellis Dec 31 '10 at 19:33
42

You define your operator << to call a virtual print method:

class Base
{
    protected:
        virtual void print(std::ostream& str) const = 0;
    public:
        friend std::ostream& operator<<(std::ostream& str, Base const& data)
        {
            data.print(str);
            return str;
        }
}
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    you probably meant `data.print(str);` – Gene Bushuyev Dec 31 '10 at 19:40
  • In addition to @Gene's comment, the pure virtual function needs to be protected so that non-friend derived classes can implement it. – Daniel Trebbien Dec 31 '10 at 20:27
  • 1
    @Daniel Trebbien: You could just as easily leave it as private and it will still be implementable. But I agree protected is probably a good idea. – Martin York Dec 31 '10 at 21:40
  • 1
    Interesting! I didn't think that that was legal, but it apparently works: http://codepad.org/NGOA87Bn – Daniel Trebbien Dec 31 '10 at 21:49
  • 1
    @Daniel Trebbien: In the derived class it can be public or private and it will still work (Java/C# are more strict about this. You can not make something less protected in a derived class). Though it is considered bad practice to make the access less restrictive in a derived class. – Martin York Dec 31 '10 at 22:38
3

It looks like you really want to provide output functionality for a hierarchy of classes, and if so, you can provide a friend operator << that calls a virtual function.

class Parent
{
public:
    friend std::ostream& operator<< (std::ostream& os, const Parent& p);
    // ... other class stuff
protected:
    virtual void printMyself(std::ostream& os) const
    {
        // do something if you must, or make this a pure virtual
    }
};

std::ostream& operator<< (std::ostream& os, const Parent& p)
{
    p.printMyself(os);
    return os;
}

class Child : public Parent
{
    // other class stuff...
protected:
    virtual void printMyself(std::ostream os) const
    {
        // whatever you need to do
    }
};

Also detailed in the C++ FAQ

wkl
  • 77,184
  • 16
  • 165
  • 176