[我听说std::bit_cast
将在C ++ 20中使用,而我的结论是,实现它必然需要特殊的编译器支持,对此我有些困惑。
[公平地说,我所听到的论点是该实现执行std::bit_cast
操作,并且memcpy
通常不是memcpy
,而应该假设是constexpr
,因此使std::bit_cast
std::bit_cast
据说需要编译器支持兼容constexpr
的constexpr
操作。
但是,我想知道是否有可能实现合规的memcpy
(即,定义的行为,与使用bit_cast
会具有定义的行为的程度相同)而根本不实际调用memcpy
。
请考虑以下代码:
memcpy
这里使用可变成员来强制编译器发出将要从成员中写入或读取的必要代码,从而禁用优化,并且尽管我知道通常分配给一个联合成员并从同一联合中的另一个成员读取是不确定的因此,C ++标准似乎确实允许从联合体[[IF的任何成员中读取,该成员已从完全相同的联合体的另一个实例按字节复制。在上面的代码中,这是通过在刚巧初始化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;
}
数据成员的新构造实例上显式调用默认副本构造函数来有效实现的。由于上述联合包含所有琐碎的类型,因此在其上调用默认副本构造函数就等于按字节复制,因此从新构造实例的in
成员进行读取不应仍然是未定义的行为,应该吗?
out
:应用于引用联合的非活动成员或其子对象的glvalue的从左值到右值的转换;
您的实现会做什么,因此这不是可行的实现策略。
这里使用易失性成员来强制编译器发出必要的代码
如果您需要编写[expr.const]/4.9以确保编译器未优化您的代码,则您的代码不正确。编译器无法修改有效代码的可观察行为。如果已经定义了要执行的操作(无volatile
),则不允许编译器优化要使用volatile进行的写入和读取。
UB的事实是,您只被允许读取一个工会的活跃成员(在您的示例中为volatile
,但是您读取了一个无效的工会(在示例中为in
)。