Это reinterpret_cast проблематично c в принципе и / или на практике? - PullRequest
4 голосов
/ 27 февраля 2020

Возьмем следующее:

#include <vector>
#include <string>
#include <type_traits>

int main()
{
    std::vector<std::string> vec{"foo", "bar"};
    for (auto& el : vec)
       el.std::string::~string();

    auto& aliased = reinterpret_cast<
       std::vector<
          std::aligned_storage_t<sizeof(std::string), alignof(std::string)>
       >&>(vec);
    aliased.clear();
}

(конечно, без более сложного кода - в таком простом тестовом примере мы вообще не будем управлять простым вектором std::string таким образом)

Есть ли в этой программе неопределенное поведение? Я думал, что мы не можем использовать псевдоним vector<T1> как vector<T2>, даже если T1 и T2 совместимы.

И если это так, можно ли ожидать, что это будет иметь практические последствия во время выполнения?

Предположим, что строгий псевдоним не отключен в компиляторе.

Интересно, что G CC 9.2.0 не дает мне никаких предупреждений с -fstrict-aliasing -Wstrict-aliasing ( live демо ).

Ответы [ 3 ]

9 голосов
/ 27 февраля 2020

Эта программа имеет неопределенное поведение?

Абсолютно. Вы получаете доступ к объекту типа vector<string> через ссылку на некоторый не связанный тип. Это UB, нарушение правила строгого алиасинга .

И если так, можно ли ожидать, что это будет иметь практические последствия во время выполнения?

UB означает, что поведение среды выполнения не определено. Так что да.

1 голос
/ 27 февраля 2020

Мой вывод из ответов и комментариев до сих пор (некоторые из которых были удалены ☹️) заключается в том, что, хотя строгий псевдоним определен в терминах доступов и, следовательно, применимо только к скалярным типам (которые в текущем черновике более понятны ), рассматриваемая программа по-прежнему несомненно имеет неопределенное поведение:

[class.mfct.non-static]/2: если для объекта, не относящегося к типу X или типа, производного от X, вызывается нестатическая c функция-член класса X, поведение не определено .

И поэтому, даже если компилятор решит не удалять оператор clear() (или иным образом делать «странные» вещи), мы не сможем гарантировать, что компоновка и работа цели Специализация vector совпадает со специализацией исходного типа, и поэтому этот шаблон следует избегать в общем производственном коде.

Так что, каким бы способом вы его ни вращали, это проблематично c в принципе и, возможно, проблематично c на практике.

(Все ссылки на n4659, который в основном C ++ 17.)

1 голос
/ 27 февраля 2020

Я думаю, что Walnut имеет примерно правильный ответ.

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

Это важно для совместимости C. В C, struct Foo { int a; } и struct Bar { int b; } могут использоваться псевдонимы друг друга.

В этом случае это неопределенное поведение, поскольку стандарт не определяет скалярные члены std::string или std::vector. std::aligned_storage_t может быть массивом скаляров, но это также не гарантируется.

...