Const перегрузка и полиморфизм - PullRequest
3 голосов
/ 26 июня 2019

У меня есть функция-член accessor (например, operator[]), которая перегружена const:

class Container {

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

Здесь const Foo& operator[] const определяется таким образом, чтобы одно и то же не определялось дважды.

Теперь я хочу сделать Container базовым классом, а operator[] станет виртуальным:

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);
};

Поскольку это незаконно для const_cast с const DerivedContainer * до BaseContainer *, я не уверен, работает ли это в случае полиморфизма.

Я бы предположил, что приведение все еще в силе, потому что тип this всегда будет const BaseContainer * в BaseContainer::operator[] const, потому что он не виртуальный, но я не уверен, что это правильный способ сделать это. Может быть, в этом случае просто лучше определить operator[] дважды?

Ответы [ 2 ]

4 голосов
/ 26 июня 2019

предполагает, что const_cas t все еще действует, потому что тип этого всегда будет const BaseContainer * в BaseContainer::operator[] const, потому что это не virtual, но я не уверен, что это правильный путьделать это.

Ваше понимание верно.Код должен работать так, как задумано.

Есть еще одна вещь, о которой вы должны подумать.Когда вы объявляете

Foo& operator[](int i);

в производном классе, версия const не будет найдена, если вызов функции сделан для объекта / ссылки / указателя производного класса.Чтобы иметь возможность использовать его с производным классом объект / ссылка / указатель, добавьте следующее в производный класс.

using BaseContainer::operator[];
0 голосов
/ 26 июня 2019

Перегруженная неконстантная версия в DerivedContainer будет вызываться из тела BaseContainer::const operator[] из-за полиморфизма. Таким образом, с этой точки зрения это на самом деле «законный» дизайн, хотя, по вашему предположению, это всегда будет const BaseContainer * в BaseContainer :: operator [] const, поскольку он не является виртуальным », - в контексте полиморфизма - неверно.

См. Следующий код, иллюстрирующий цепочку вызовов:

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;
}

Обратите внимание, что неконстантное тело operator[] не должно изменять объект *this, если этот объект изначально был определен как const. В противном случае вы получите неопределенное поведение.

...