возможно ли bit_cast без поддержки компилятором memtex для constexpr? - PullRequest
1 голос
/ 21 октября 2019

Я слышал, что std::bit_cast будет в C ++ 20, и я немного озадачен выводом о том, что его реализация обязательно требует специальной поддержки компилятора.

Чтобы быть справедливымЯ слышал аргумент, что реализация выполняет операцию memcpy, и memcpy обычно не constexpr, в то время как std::bit_cast, как предполагается, поэтому для std::bit_cast constexpr предположительно требуется поддержка компилятора дляconstexpr -совместимая memcpy операция.

Мне было интересно, однако, можно ли реализовать совместимый bit_cast (т. Е. Определенное поведение в той же степени, что использование memcpy имело бы определенное поведение) без фактического вызова memcpy вall.

Рассмотрим следующий код:

template<typename T, typename U> 
inline constexpr T bit_cast(const U & x) noexcept {
    static_assert(std::is_trivial<T>::value && std::is_trivial<U>::value, "Cannot use bit_cast with non-trivial data" );
    static_assert(sizeof(T) == sizeof(U), "bit_cast must be used on identically sized types");
    union in_out {
        volatile U in;
        volatile T out;

        inline constexpr explicit in_out(const U &x) noexcept : in(x)
        {
        }

    };
    return in_out(in_out(x)).out;
}

Здесь используются энергозависимые элементы, чтобы заставить компилятор выдавать необходимый код, который будет записываться или считываться из элементов, отключая оптимизацию и, в то время какЯ знаю, что обычно присваивание одному члену объединения и чтение из другого в том же объединении является неопределенным поведением, стандарт C ++, по-видимому, позволяет читать из любого члена объединения IF , он был побайтово скопирован из другогоэкземпляр точно такого же союза. В приведенном выше коде это эффективно достигается путем явного вызова конструктора копирования по умолчанию для вновь созданного экземпляра, который инициализирует элемент данных in. Поскольку вышеприведенное объединение содержит все тривиальные типы, вызов конструктора копирования по умолчанию составляет для него байтовую копию, поэтому чтение из члена out вновь созданного экземпляра по-прежнему не должно быть неопределенным поведением, не так ли?

Конечно, вполне возможно, что я здесь упускаю что-то чрезвычайно очевидное ... Я, конечно, не могу утверждать, что я умнее людей, которые разрабатывают эти стандарты, но если кто-то может сказать мне, какое именно неопределенное поведение я используюздесь я бы очень хотел узнать об этом.

Ответы [ 2 ]

4 голосов
/ 21 октября 2019

Одна из вещей, которую вы не можете делать во время константной оценки, это: [expr.const] /4.9:

преобразование lvalue в rvalueэто применяется к значению glvalue, которое относится к неактивному члену объединения или его подобъекту;

Именно это и делает ваша реализация, поэтому это не жизнеспособная стратегия реализации.

2 голосов
/ 21 октября 2019

Здесь используются переменные, чтобы заставить компилятор выдавать необходимый код

Если вам нужно написать volatile, чтобы компилятор не оптимизировал ваш код, тогдаваш код не в порядке. Компилятор не может изменить наблюдаемое поведение допустимого кода. Если бы то, что вы хотели сделать (sans volatile), было бы определенным поведением, компилятору не разрешили бы оптимизировать записи и чтения, которые вы хотите форсировать с помощью volatile.

UB происходит отфакт, что вам разрешено только читать активный член объединения (in в вашем примере), но вы читаете неактивный (out в вашем примере).

...