没有编译器支持 constexpr memcpy 的 bit_cast 可能吗?

mar*_*964 3 c++ undefined-behavior type-punning constexpr c++20

我听说这std::bit_cast将在 C++20 中实现,我对实现它必然需要特殊编译器支持的结论感到有些困惑。

公平地说,我听到的论点是实现执行了一个memcpy操作,并且memcpy通常不是constexpr,而std::bit_cast应该是,因此制作std::bit_cast constexpr据称需要编译器支持constexpr兼容的memcpy操作。

但是,我想知道是否有可能在根本不实际调用的情况下实现合规bit_cast(即定义的行为,其程度与 usingmemcpy具有定义的行为相同)memcpy

考虑以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

这里使用可变成员来强制编译器发出必要的代码,这些代码将从成员中写入或读取,禁用优化,虽然我知道通常分配给联合的一个成员并从同一联合中的另一个成员读取是未定义的行为, C++ 标准似乎允许从联合的任何成员读取,如果它是从完全相同联合的另一个实例按字节复制的。在上面的代码中,这是通过在刚好初始化in数据成员的新构造实例上显式调用默认复制构造函数来有效完成的。由于上述联合包含所有平凡类型,因此对其调用默认复制构造函数相当于按字节复制,因此从out 新构造的实例的成员不应该仍然是未定义的行为,是吗?

当然,我完全有可能在这里遗漏了一些非常明显的东西......我当然不能声称自己比制定这些标准的人更聪明,但是如果有人能确切地告诉我我在这里调用的是哪种未定义的行为,我真的很想知道。

Bar*_*rry 6

在不断评估期间你不允许做的一件事是,来自[expr.const]/4.9

左值到右值的转换,应用于引用联合或其子对象的非活动成员的泛左值;

这就是您的实施所做的,因此它不是可行的实施策略。