Yifan Lai
Yifan Lai

Reputation: 141

Const overloading and polymorphysm

I have an accessor member function (e.g. operator[]) that is const-overloaded:

class Container {

public:
    Foo&       operator[](int i);
    const Foo& operator[](int i) const{
        return const_cast<Container *>(this)->operator[](i);
    }
};

Here, const Foo& operator[] const is defined in this way so that the same thing is not defined twice.

Now I want to make Container a base class, and operator[] becomes virtual:

class BaseContainer {

public:
    virtual Foo& operator[](int i) = 0;
    const Foo& operator[](int i) const{
        // Is this correct?
        return const_cast<BaseContainer *>(this)->operator[](i);
    }
};

class DerivedContainer : public BaseContainer {
public:
    Foo& operator[](int i);
};

Since it is illegal to const_cast from const DerivedContainer * to BaseContainer *, I am not sure if this works in the case of polymorphism.

I would assume that the cast is still valid because the type of this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual, but I am not sure if that's the correct way of doing this. Maybe it is simply better to define operator[] twice in this case?

Upvotes: 3

Views: 98

Answers (2)

Stephan Lechner
Stephan Lechner

Reputation: 35154

The overloaded non-const-version in DerivedContainer will be called from the body of BaseContainer::const operator[] due to polymorphism. So from this point it is actually a "legal" design, although your assumption "this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual" is - in the context of polymorphism - wrong.

See the following code illustrating the call chain:

struct Base {
    virtual void print() { cout << "Base.non-const;"; }
    void print() const { cout << "entry:Base.const;then..."; const_cast<Base *>(this)->print(); }
};

struct Derived : public Base {
    void print() override { cout << "Derived.non-const;"; }
};

int main() {

    const Base* bc = new Derived;
    bc->print();
    //Output: entry:Base.const;then...Derived.non-const;

    cout << endl;

    Base* bnc = new Derived;
    bnc->print();
    // Output: Derived.non-const;
}

Note that the non-const-body of operator[] must not alter the *this-object if this object has originally been defined as const. Otherwise you get undefined behaviour.

Upvotes: 0

R Sahu
R Sahu

Reputation: 206567

would assume that the const_cast is still valid because the type of this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual, but I am not sure if that's the correct way of doing this.

Your understanding is correct. The code should work as intended.

There is another thing you have to think about, though. When you declare

Foo& operator[](int i);

in the derived class, the const version is not going to be found if the function call is made on a derived class object/reference/pointer. To be able to use it with a derived class object/reference/pointer, add the following in the derived class.

using BaseContainer::operator[];

Upvotes: 4

Related Questions