我正在学习现代 C++,目前正在阅读有关模板和自动类型推导的内容。
作为一项挑战,我尝试编写书中的函数来解决问题,但最终结果与书中建议的略有不同。
练习: 编写一个返回枚举整数值的模板函数。
我的职能:
template<typename T> constexpr auto EnumToIntegral(T enumerator) noexcept { return std::underlying_type_t<T>(enumerator); }
书籍功能:
template<typename T> constexpr auto EnumToIntegral(T enumerator) noexcept { return static_cast<std::underlying_type_t<T>>(enumerator); }
这里唯一的区别是
static_cast
,我不明白其中的意义。
这两个函数都按预期编译并运行。
查看有关获取整数枚举值的帖子中的其他片段,我还看到使用了
static_cast
,但我找不到任何理由。
我尝试过使用和不使用
static_cast
,但我得到了相同的结果。
枚举值的类型为 int
,使用此结果的函数是 std::get
。
C++ 中的基本类型转换为
static_cast
、const_cast
、reinterpret_cast
和 dynamic_cast
。
其中每一个都能够将某些类型和值类别的表达式转换为某些其他类型。它们每个允许的转换集是不同的,有时它们与不同的语义重叠。
该语言中还存在 C 风格的显式强制转换,但除了一个小例外之外,它只是前面提到的强制转换的抽象。
C 风格的强制转换将执行以下列表中可能的第一个强制转换序列:
static_cast
const_cast
static_cast
随后是 const_cast
reinterpret_cast
reinterpret_cast
随后是 const_cast
此外,C 风格的转换允许此选择中的
static_cast
忽略基类的可访问性,即它可以将派生类指针转换为私有基类指针,而该语言中的其他构造不允许。
因此,C 风格的强制转换是危险的,因为它们可以将几乎所有内容强制转换为其他所有内容,但很难确定语义并避免错误。
现在的问题是这种显式转换有两种不同的语法形式:
(T)e
T(e)
其中
T
是目标类型,e
是源表达式。第一种形式众所周知,因为它是 C 使用的语法,但第二种形式令人困惑,因为它看起来类似于
T(e1, e2)
T()
T{e}
其中
e1
和 e2
又是表达式。这些是“直接初始化”而不是强制转换表达式。 仅如果在圆括号而不是大括号中使用单个表达式,则它是具有上述功能的显式强制转换。
因此,如果想要在避免 C 风格强制转换方面保持一致,还应该避免使用 T(e)
形式的表达式。
但这就是你正在使用的。static_cast
更明确,说明您想要什么基本转换,而不是编译器应该尝试上面提到的列表并可能做出错误的选择。
在此特定示例中没有区别,因为具有枚举类型的表达式始终可以使用static_cast
转换为其基础类型,以便保证选择列表中的第一个条目。但这可能并不明显。
实际上,形式T{e}
(
direct-list-initialization) 甚至比
static_cast
更弱,但仍然比隐式转换强,但它在这里还不够,因为它不允许转换作用域枚举类型。