没有编译器支持Constexpr Memcpy的bit_cast可能吗?

is bit_cast without compiler support for constexpr memcpy possible?

本文关键字:cast bit 编译器 支持 Constexpr Memcpy      更新时间:2023-10-16

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

公平地说,我听到的论点是实现执行memcpy操作,并且memcpy通常不是constexpr,而std::bit_cast应该是,因此制作std::bit_castconstexpr应该需要编译器支持符合constexprmemcpy操作。

但是,我想知道是否可以在不实际调用memcpy的情况下实现合规bit_cast(即定义的行为,与使用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;
}

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

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

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

应用于指代联合或其子对象的非活动成员的 glvalue 的左值到右值转换;

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

此处使用易失性成员来强制编译器发出必要的代码

如果您需要编写volatile以确保编译器不会优化您的代码,那么您的代码是不行的。编译器无法修改有效代码的可观察行为。如果你想做的(没有volatile(是定义的行为,编译器将不允许优化你想要强制使用易失性的写入和读取。

UB 来自这样一个事实,即您只被允许读取联合的活动成员(在您的示例中in(,但您读取非活动成员(在您的示例中out(。