reinterpret_cast ссылки на shared_ptr - PullRequest
0 голосов
/ 04 октября 2019

Пусть B будет производным от класса A. Когда я читаю различные посты, у меня складывается впечатление, что приведение, как в

    const std::shared_ptr<const A> a(new B());
    const std::shared_ptr<const B>& b = reinterpret_cast<const std::shared_ptr<const B>&>(a);

, по какой-то причине не рекомендуется, и вместо этого следует использовать reinterpret_pointer_cast. Однако я хотел бы избежать создания нового shared_ptr по соображениям производительности. Законен ли приведенный выше код? Приводит ли это к неопределенному поведению? Кажется, что работает в gcc и в Visual Studio.

Ответы [ 4 ]

1 голос
/ 05 октября 2019

Вы хотите static_pointer_cast .

const std::shared_ptr<const A> a(new B());
const std::shared_ptr<const B> b = std::static_pointer_cast<const B>(a);

Я очень сомневаюсь, что вышеизложенное вызовет какие-либо проблемы с производительностью. Но если у вас есть доказательства того, что shared_ptr создает проблему с производительностью, откат к необработанному указателю:

    const B* pB = static_cast<const B*>(a.get());

Еще один совет. Пожалуйста, старайтесь избегать reinterpret_cast между классами с наследственными отношениями. В тех случаях, когда существуют виртуальные методы и / или множественное наследование, static_cast будет корректно корректировать смещение указателя в соответствии с правильным vtable или базовым смещением. Но reinterpret_cast не будет. (Или технически: неопределенное поведение )

0 голосов
/ 10 октября 2019

Использование функций shared_ptr или любого другого стандартного класса или шаблона определяется только при вызове функций (включая функции-члены) для класса типа, который вы передаете функции (в том числе как неявный this). аргумент):

Ничто в стандарте не определяет, что происходит, когда вы вызываете стандартную функцию, ожидающую Foo и передающую Bar, для любых двух стандартных типов Foo и Bar (или даже для пользовательских типов.)

Это не определено;это un определено. Не соблюдая самое основное условие: использовать аргументы правильного типа.

0 голосов
/ 07 октября 2019

Сначала вы создаете объект a типа const std::shared_ptr<const A> a и инициализируете его указателем на некоторый тип B. Это работает, только если вы можете присвоить B* для A*, поэтому должны быть такие отношения, как наследование. Игнорируя это, вы преобразуете объект одного типа в ссылку на другой тип с помощью reinterpret_cast:

Выражение glvalue типа T1 может быть приведено к типу «ссылка на T2», если выражение типа«Указатель на T1» может быть явно преобразован в тип «Указатель на T2» с помощью reinterpret_cast. Результат ссылается на тот же объект, что и у источника glvalue, но с указанным типом. [Примечание: то есть для l-значений эталонное приведение reinterpret_cast (x) имеет тот же эффект, что и преобразование * reinterpret_cast (& x) со встроенными операторами & и * (и аналогично для reinterpret_cast (x)). - конец примечания]

Для указателей reinterpret_cast сводится к преобразованию в void*, а затем к целевому типу:

Указатель объекта может быть явно преобразован вуказатель объекта другого типа. 72 Когда значение v указателя типа объекта преобразуется в указатель объекта типа «указатель на cv T», в результате получается static_cast<cv T*>(static_cast<cv void*>(v)).

Семантикадва статических приведения определены как:

Значение типа «указатель на пустоту cv1» может быть преобразовано в значение типа «указатель на cv2 T», где T - это тип объекта, а cv2 - этота же квалификация cv, что и cv1 или более высокая, чем cv1. Значение нулевого указателя преобразуется в значение нулевого указателя типа назначения. Если исходное значение указателя представляет адрес A байта в памяти, и A удовлетворяет требованию выравнивания T, то результирующее значение указателя представляет тот же адрес, что и исходное значение указателя, то есть A. Результат любого другого такого указателяпреобразование не указано.

Платформа, на которой я работаю, имеет ближний и дальний указатели, которые являются 16 или 32-битными. В этом случае типы shared_ptr<A> и shared_ptr<B> имеют разный размер и выравнивание, и приведение одного к другому является неопределенным поведением. Если выравнивание совпадает, результат статического приведения определен.

Однако, первое предложение о reinterpret_cast для ссылки также содержит примечание

[ Note: That is, for lvalues, a reference
cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with
the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). —end note ]

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

Если программа пытается получить доступ к сохраненному значениюДля объекта через glvalue, отличного от одного из следующих типов, поведение не определено: 53 - динамический тип объекта, - cv-квалифицированная версия динамического типа объекта, - аналогичный тип (как определено в 4.4)к динамическому типу объекта, - тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта, - тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа объектаобъект, - агрегатный или объединенный тип, который включает один из вышеупомянутых типов среди своих элементов или нестатических элементов данных (включая, рекурсивно, элемент или нестатический элемент данных субагрегата или автономного объединения),

0 голосов
/ 05 октября 2019

reinterpret_cast обычно приводит к UB. Иногда вы готовы рискнуть использовать его по причинам производительности, но вы будете стараться избегать такого рода вещей как можно больше. В этом случае вам лучше использовать static_pointer_cast.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...