bit_cast 没有编译器支持 constexpr memcpy 可能吗?
is bit_cast without compiler support for constexpr memcpy possible?
听说std::bit_cast
会出现在C++20中,对于实现它必然需要特殊编译器支持的结论我有点疑惑
公平地说,我听到的说法是该实现执行了一个 memcpy
操作,而 memcpy
通常不是 constexpr
,而 std::bit_cast
应该是是的,因此使 std::bit_cast
constexpr
据说需要编译器支持 constexpr
兼容的 memcpy
操作。
不过,我想知道是否有可能在不实际调用 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;
}
此处使用可变成员来强制编译器发出必要的代码,这些代码将写入成员或从成员读取,禁用优化,而我知道通常分配给联合的一个成员并从同一成员读取另一个成员union 是未定义的行为,C++ 标准似乎允许从 union IF 的任何成员读取它已从完全相同的 union 的另一个实例按字节复制。在上面的代码中,这是通过在恰好初始化 in
数据成员的新构造实例上显式调用默认复制构造函数来有效实现的。由于上面的 union 包含所有普通类型,调用它的默认复制构造函数相当于按字节复制,所以从新构造的实例的 out
成员读取不应该仍然是未定义的行为,不是吗?
当然,我完全有可能在这里遗漏了一些非常明显的东西......我当然不能声称比制定这些标准的人更聪明,但如果有人能准确地告诉我哪个未定义我在这里调用的行为,我真的很想知道它。
在持续评估期间你不能做的事情之一是,来自 [expr.const]/4.9:
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
您的实施是做什么的,因此它不是可行的实施策略。
Volatile members are used here to force the compiler to emit the necessary code
如果您需要编写 volatile
以确保编译器不会优化您的代码,那么您的代码不正确。编译器无法修改有效代码的可观察行为。如果你想做的事情(sans volatile
)已经被定义为行为,编译器将不会被允许优化你想要用 volatile 强制执行的写入和读取。
UB 来自这样一个事实,即您只被允许读取联合的活跃成员(在您的示例中为 in
),但您读取了不活跃的成员(在您的示例中为 out
) .
听说std::bit_cast
会出现在C++20中,对于实现它必然需要特殊编译器支持的结论我有点疑惑
公平地说,我听到的说法是该实现执行了一个 memcpy
操作,而 memcpy
通常不是 constexpr
,而 std::bit_cast
应该是是的,因此使 std::bit_cast
constexpr
据说需要编译器支持 constexpr
兼容的 memcpy
操作。
不过,我想知道是否有可能在不实际调用 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;
}
此处使用可变成员来强制编译器发出必要的代码,这些代码将写入成员或从成员读取,禁用优化,而我知道通常分配给联合的一个成员并从同一成员读取另一个成员union 是未定义的行为,C++ 标准似乎允许从 union IF 的任何成员读取它已从完全相同的 union 的另一个实例按字节复制。在上面的代码中,这是通过在恰好初始化 in
数据成员的新构造实例上显式调用默认复制构造函数来有效实现的。由于上面的 union 包含所有普通类型,调用它的默认复制构造函数相当于按字节复制,所以从新构造的实例的 out
成员读取不应该仍然是未定义的行为,不是吗?
当然,我完全有可能在这里遗漏了一些非常明显的东西......我当然不能声称比制定这些标准的人更聪明,但如果有人能准确地告诉我哪个未定义我在这里调用的行为,我真的很想知道它。
在持续评估期间你不能做的事情之一是,来自 [expr.const]/4.9:
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
您的实施是做什么的,因此它不是可行的实施策略。
Volatile members are used here to force the compiler to emit the necessary code
如果您需要编写 volatile
以确保编译器不会优化您的代码,那么您的代码不正确。编译器无法修改有效代码的可观察行为。如果你想做的事情(sans volatile
)已经被定义为行为,编译器将不会被允许优化你想要用 volatile 强制执行的写入和读取。
UB 来自这样一个事实,即您只被允许读取联合的活跃成员(在您的示例中为 in
),但您读取了不活跃的成员(在您的示例中为 out
) .