Размещение нового базового подобъекта производного в C ++ - PullRequest
0 голосов
/ 19 октября 2018

Определено ли поведение для размещения нового тривиально разрушаемого базового объекта производного?

struct base { int& ref; };
struct derived : public base {
    complicated_object complicated;
    derived(int& r, complicated_arg arg) :
            base {r}, complicated(arg) {}
};

unique_ptr<derived> rebind_ref(unique_ptr<derived>&& ptr,
                               int& ref) {
    // Change where the `ref` in the `base` subobject of
    // derived refers.
    return unique_ptr<derived>(static_cast<derived*>(
        ::new (static_cast<base*>(ptr.release()) base{ref}));
}

Обратите внимание, что я попытался структурировать rebind_ref, чтобы не нарушать какие-либо строгие предположения в отношении псевдонимов, которые компилятор мог сделать.

Ответы [ 2 ]

0 голосов
/ 21 октября 2018
static_cast<derived*>(
        ::new (&something) base{ref})

недопустимо, по определению new (...) base(...) создает объект base в качестве нового завершенного объекта, который иногда может рассматриваться как существующий завершенный объект или член подобъект (в условиях, которые неbase в любом случае), но никогда не является базовым подобъектом.

Не существует существующего правила , в котором говорится, что вы можете притворяться, что new (addr) base создает действительный производный объект только потому, что base объект перезаписывает другой base базовый подобъект.Если ранее был объект derived, вы просто повторно использовали хранилище с new (addr) base.Даже если по какой-то магии объект derived все еще существует, результат вычисления нового выражения не будет указывать на него, он будет указывать на base завершенный объект.

Если выЕсли вы хотите притвориться, что вы что-то сделали (например, создали объект derived), фактически не делая этого (вызывая конструктор derived), вы можете добавить некоторые указатели volatile на указатели, чтобы заставить компилятор стереть все предположения о значениях искомпилируйте код, как если бы произошел переход ABI.

0 голосов
/ 21 октября 2018

Нет, это не разрешено Стандартом C ++, по крайней мере по двум причинам.

Текст, который иногда допускает размещение нового объекта в хранилище для другого объекта того же типа,встречается в [basic.life], пункт 8 .Выделение жирным шрифтом - мое.

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

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и

  • новый объект того же типа, что и исходный объект (без учета cv-квалификаторов верхнего уровня), а

  • тип исходного объекта не является константно-квалифицированным, и,если тип класса, не содержит ни одного нестатического члена данных, тип которого const-qualified или ссылочный тип и

  • [C ++ 17] исходный объект был наиболее производным объектом типа T, а новый объект является наиболее производным объектомтипа T (то есть они не являются подобъектами базового класса ).

  • [C ++ 20 черновик 2018-10-09] ни исходный объект, ни новый объект не являются потенциально перекрывающимися подобъектами ([intro.object]).

Изменение C ++ 20 должно учитыватьвозможность использования нестатических элементов данных нулевого размера, но это также исключает все подобъекты базового класса (пустые или нет).«Потенциально перекрывающийся подобъект» - это новый термин, определенный в [intro.object] параграфе 7 :

A потенциально перекрывающийся подобъект - это либо:

  • подобъект базового класса или

  • элемент нестатических данных, объявленный с атрибутом no_unique_address ([dcl.attr.nouniqueaddr]).

(Даже если вы найдете способ перекомпоновки вещей, чтобы избежать проблем со ссылочным членом и базовым классом, не забудьте, что никто не сможеткогда-нибудь определите переменную const derived, например, сделав все конструкторы приватными!)

...