Четко ли определено разделение объектов путем назначения через базовую ссылку?

Является ли нарезка объекта, которая происходит при назначении производного объекта через ссылку на базовый класс (без виртуального operator=), четко определенной операцией? То есть гарантирует ли стандарт, что производные части объекта останутся нетронутыми?

Во многих вопросах, касающихся нарезки объектов здесь и в других местах, приводится пример, подобный следующему:

struct Base{
    int x;
    Base(int xx) :x(xx) {}
    virtual void print() const {std::cout << "Base("<<x<<")\n";}
};

struct Derived : Base{
    int y;
    Derived(int xx, int yy ) :Base(xx),y(yy){}
    void print() const {std::cout << "Derived("<<x<<","<<y<<")\n";}
};

int main()
{
    Derived d1{1,2};
    Derived d2{3,4};

    Base& br = d1;
    br = d2;     // assign a Derived through a Base&

    br.print();  // prints Derived(3,2)
}

Пример предназначен для того, чтобы показать, что при назначении через Base& он назначает только члены класса Base и оставляет члены в Derived нетронутыми.

Сборка вышеприведенного примера с помощью -fsanitize=undefined не жалуется и дает ожидаемый результат на всех системах, на которых я его запускал.

Вопрос в том, гарантируется ли это стандартом? В моем понимании это так, поскольку Base::operator= не может писать вне Base части объекта, а Derived часть объекта (здесь int y) не может иметь никакого пересечения с Base частью.

Есть ли какой-то угловой случай, который мне не хватает? (Конечно, существует много способов, которыми наличие несогласованного объекта может привести к неопределенному поведению, но мой вопрос ограничивается тем, что происходит во время операции присваивания.)


person drRobertz    schedule 27.04.2020    source источник


Ответы (2)


Base& br = d1;

Это создает ссылку на объект Base. Теперь это будет ссылка на объект Base, который имеет такой же правильный формат, как и любой другой объект Base (в правильно сформированной программе на C++). Во всех отношениях указанный объект Base идентичен всем другим объектам Base, которые могут существовать или не существовать. Этот объект может быть назначен так же, как и любой другой объект Base, и с этим объектом произойдет то же самое, что и с любым другим объектом Base, которому назначено. Конец.

Объекты, которые являются базовыми объектами других объектов, ничем не отличаются от объектов, которые таковыми не являются, если смотреть только через призму этих объектов. Они являются собственными объектами правильной формы и ничем не отличаются от любых других объектов Base (здесь игнорируются такие вещи, как виртуальные методы). Это фундаментальная концепция C++.

Было бы довольно неудобно, если бы присваивание базовому объекту вдруг "записало" куда-то еще. В этом случае было бы довольно сложно сделать что-либо на C++. Разумеется, всегда можно перегрузить operator=, и тогда нет предела. Но с практической точки зрения, пока оператор = работает так, как от него ожидают, присваивание объекту Base ничего не сделает ни с чем, что не является частью этого объекта Base.

person Sam Varshavchik    schedule 27.04.2020

Четко ли определено разделение объектов путем назначения через базовую ссылку?

Да. Нарезка — это четко определенная операция.

гарантируется ли стандартом, что производные части объекта останутся нетронутыми?

Неявно сгенерированный оператор присваивания базы не будет касаться производных частей. Это можно сделать на заказ, хотя такой дизайн встречается редко.

person eerorika    schedule 27.04.2020