Search code examples
c++inheritancevirtualoverriding

Why is this C++ inheritance code sample behaves like this


I have a code sample which behaves strange for me. With inheritance in C++ one can declare array of pointers to base class with pure virtual function (aka Interface) and call derived member functions over it;

class Base {

public:

    virtual void call() = 0;

};

class Derived1 : public Base {

public:

    void call() override final {

        std::wcout << L"derived 1" << std::endl;

    }

};

class Derived2 : public Base {

public:

    void call() override final {

        std::wcout << L"derived 2" << std::endl;

    }

};

int main() {

    Base* b[2];    

    b[0] = new Derived1;
    b[1] = new Derived2;

    for (int i = 0; i < 2; ++i) {

        b[i]->call();

    }

    return 0;

}

Which gives:

derived 1
derived 2

just as planned. But when I trying following code sample it makes me a little bit confusing:

class Base {

public:

    virtual Base* print() = 0;

    template<typename T>
    Base& operator<<(const T &_val) {

        std::wcout << L" d0 << " << _val;
        return *this;

    }

};

class Derived1 : public Base {

public:

    Derived1* print() override final {

        return this;

    }

    template<typename T>
    Derived1& operator<<(const T &_val) {

        std::wcout << L" d1 << " << _val;
        return *this;

    }

};

class Derived2 : public Base {

public:

    Derived2* print() override final {

        return this;

    }

    template<typename T>
    Derived2& operator<<(const T &_val) {

        std::wcout << L" d2 << " << _val;
        return *this;

    }

};

int main() {

    Base* b[2];

    b[0] = new Derived1;
    b[1] = new Derived2;

    for (int i = 0; i < 2; ++i) {

        std::wcout << typeid(*b[i]->print()).name();
        *b[i]->print() << 7 << 7;
        std::wcout << std::endl;

    }

    return 0;

}

The output is:

8Derived1 d0 << 7 d0 << 7
8Derived2 d0 << 7 d0 << 7

Which means that only Base's operator<< was called (But prints() return types seems to be correst).

The question is why it behaves like so?

UPD:


Seems like I need static polymorphism here without virtual functions. But how could this be achieved? I need an array of different Derived classes to perform actions in operator<< on any data type. UPD2:


Looks like I can use type erasure for operator<< parameter. But how can I restore type inside derived's operator<< then? (For example if I suggest to use boost::any)


Solution

  • Your operator << () is not virtual, and so, if you call this operator of a base class pointer, always the base class implementation is called.

    That's because it's a template method, and template methods can't be virtual (see Can a C++ class member function template be virtual?).

    Solution would be to write virtual specialized operator << () for each supported data type (if you really want them to differ between inherited classes).

    Here's the changed code:

    class Base {
    
    public:
    
        virtual Base* print() = 0;
    
        virtual Base& operator<<( int _val ) = 0;
    
    };
    
    class Derived1 : public Base {
    
    public:
    
        Derived1* print() override final {
    
            return this;
    
        }
    
        Base& operator<<( int _val ) override final {
    
            std::wcout << L" d1 << " << _val;
            return *this;
    
        }
    
    };
    
    class Derived2 : public Base {
    
    public:
    
        Derived2* print() override final {
    
            return this;
    
        }
    
        Base& operator<<( int _val ) override final {
    
            std::wcout << L" d2 << " << _val;
            return *this;
    
        }
    
    };
    
    int main() {
    
        Base* b[2];
    
        b[0] = new Derived1;
        b[1] = new Derived2;
    
        for (int i = 0; i < 2; ++i) {
    
            std::wcout << typeid(*b[i]->print()).name();
            *b[i]->print() << 7 << 7;
            std::wcout << std::endl;
    
        }
    
        return 0;
    
    }
    

    Another approach would be, to only implement the differing parts via class hierarchy (here: the class name), and to use the template operator for generic calls.

    #include <iostream>
    #include <string>
    
    class Base {
    
    public:
    
        virtual Base* print() = 0;
        virtual const wchar_t* className() const = 0;
    
        template<typename T>
        Base& operator<<(const T &_val) {
    
            std::wcout << L" " << className() << L" << " << L" << " << _val;
            return *this;
    
        }
    
    };
    
    class Derived1 : public Base {
    
    public:
        const wchar_t* className() const override final { return L"d1"; }
    
        Derived1* print() override final {
    
            return this;
    
        }
    
    };
    
    class Derived2 : public Base {
    
    public:
        const wchar_t* className() const override final { return L"d2"; }
    
        Derived2* print() override final {
    
            return this;
    
        }
    
    };
    
    int main() {
    
        Base* b[2];
    
        b[0] = new Derived1;
        b[1] = new Derived2;
    
        for (int i = 0; i < 2; ++i) {
    
            std::wcout << typeid(*b[i]->print()).name();
            *b[i]->print() << 7 << 7;
            std::wcout << std::endl;
    
        }
    
        return 0;
    
    }
    

    The last approach would be to RTTI cast according to the class type, and call the specialized operator << () according to the class type.

    #include <iostream>
    
    class Derived1;
    class Derived2;
    
    class Base {
    
    public:
    
        virtual Base* print() = 0;
    
    
    };
    
    class Derived1 : public Base {
    
    public:
    
        Derived1* print() override final {
    
            return this;
    
        }
    
        template<typename T>
        Derived1& operator<<(const T &_val) {
    
            std::wcout << L" d1 << " << _val;
            return *this;
    
        }
    
    };
    
    class Derived2 : public Base {
    
    public:
    
        Derived2* print() override final {
    
            return this;
    
        }
    
        template<typename T>
        Derived2& operator<<(const T &_val) {
    
            std::wcout << L" d2 << " << _val;
            return *this;
    
        }
    
    };
    
    template<typename T>
    Base& operator<<( Base& _base, const T &_val) {
    
        if( typeid( _base ) == typeid( Derived1 ))
            return dynamic_cast<Derived1*>(&_base)->operator << (_val);
        else
            return dynamic_cast<Derived2*>(&_base)->operator << (_val);
    }
    
    int main() {
    
        Base* b[2];
    
        b[0] = new Derived1;
        b[1] = new Derived2;
    
        for (int i = 0; i < 2; ++i) {
    
            std::wcout << typeid(*b[i]->print()).name();
            *b[i]->print() << 7 << 7;
            std::wcout << std::endl;
    
        }
    
        return 0;
    
    }