在枚举 class 上使用 `reinterpret_cast` - 有效或未定义的行为?
Using `reinterpret_cast` on an enum class - valid or undefined behavior?
#include <iostream>
#include <cassert>
#include <type_traits>
template<typename T> using Underlying = std::underlying_type_t<T>;
enum class ETest : int
{
Zero = 0,
One = 1,
Two = 2
};
template<typename T> auto& castEnum(T& mX) noexcept
{
// `static_cast` does not compile
// return static_cast<Underlying<T>&>(mX);
return reinterpret_cast<Underlying<T>&>(mX);
}
int main()
{
auto x(ETest::Zero);
castEnum(x) = 1;
assert(x == ETest::One);
return 0;
}
此代码是否保证始终有效?还是未定义的行为?
标准有点不清楚:
3.10 Lvalues and rvalues [basic.lval]
10 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
[...]
(10.4) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object,
[...]
这可以合法地理解为枚举类型对应的有符号或无符号类型是其基础类型,但我认为这意味着涵盖 仅 访问整数类型通过它们的其他符号对应类型,枚举类型的基础类型不算作与该枚举类型对应的(无)符号类型。
至少 GCC 同意这一点:它给出了
的别名警告
enum E : int { };
int f(E e) { return *(int *) &e; }
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
强烈暗示它将在您的程序中没有发生此类别名的假设下进行优化。
#include <iostream>
#include <cassert>
#include <type_traits>
template<typename T> using Underlying = std::underlying_type_t<T>;
enum class ETest : int
{
Zero = 0,
One = 1,
Two = 2
};
template<typename T> auto& castEnum(T& mX) noexcept
{
// `static_cast` does not compile
// return static_cast<Underlying<T>&>(mX);
return reinterpret_cast<Underlying<T>&>(mX);
}
int main()
{
auto x(ETest::Zero);
castEnum(x) = 1;
assert(x == ETest::One);
return 0;
}
此代码是否保证始终有效?还是未定义的行为?
标准有点不清楚:
3.10 Lvalues and rvalues [basic.lval]
10 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
[...]
(10.4) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object,
[...]
这可以合法地理解为枚举类型对应的有符号或无符号类型是其基础类型,但我认为这意味着涵盖 仅 访问整数类型通过它们的其他符号对应类型,枚举类型的基础类型不算作与该枚举类型对应的(无)符号类型。
至少 GCC 同意这一点:它给出了
的别名警告enum E : int { };
int f(E e) { return *(int *) &e; }
warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
强烈暗示它将在您的程序中没有发生此类别名的假设下进行优化。