静态constexpr int与老式枚举:何时以及为什么
Static constexpr int vs old-fashioned enum: when and why?
这可能是一个基本问题,但我现在自己看不到答案。
考虑以下代码:
template<bool b>
struct T {
static constexpr int value = (b ? 42 : 0);
};
template<bool b>
struct U {
enum { value = (b ? 42 : 0) };
};
int main() {
static_assert(T<true>::value == 42, "!");
static_assert(T<false>::value == 0, "!");
static_assert(U<true>::value == 42, "!");
static_assert(U<false>::value == 0, "!");
}
我习惯于使用像T
这样的结构,但我不止一次看到像U
这样的结构用于相同的目的(主要是特征定义)。
据我所见,它们都是在编译时解决的,而且它们几乎解决了相同的问题,但在我看来,T
比U
可读性强得多(好吧,我知道,这是我的个人观点)。
我的问题很简单:一种解决方案比另一种更好,有什么技术原因吗
更重要的是,有没有哪一种情况不是可行的解决方案?
请注意,以下答案不适用于C++17及更高版本
当这样使用时,积分常数不会有明显的差异。
然而,enum
实际上更好,因为它是一个真正的命名常数。例如,constexpr
积分常数是一个可以使用ODR的对象,这会导致链接错误。
#include <iostream>
struct T {
static constexpr int i = 42;
enum : int {x = 42};
};
void check(const int& z) {
std::cout << "Check: " << z << "n";
}
int main() {
// check(T::i); // Uncommenting this will lead to link error
check(T::x);
}
取消注释check(T::i)
时,程序无法链接:
/tmp/ccZoETx7.o
:在函数"main
"中:ccc.cpp
:(.text+0x45
):未定义引用"T::i
"collect2
:错误:ld
返回1
退出状态
然而,真正的enum
总是有效的。
我怀疑这是遗留代码。
enum { value = (b ? 42 : 0) };
是C++03和C++11中的有效代码。
static constexpr int value = (b ? 42 : 0);
仅在C++11中有效。
更重要的是,有没有哪一种情况不是可行的解决方案?
两者都是C++11中可行的解决方案。使用哪一个取决于团队。这将是一个政策决定的问题。
正如SergeyA的回答所表明的,enum
是真常数。您不能ODR使用它们。您可以使用constexpr
进行ODR。根据您的应用程序需要这些选项中的哪一个,您可以决定是使用enum
s还是constexpr
s。
SergeA目前接受的答案在C++17(定义和ODR)时不再成立。
每个声明都是一个定义,但以下声明除外:
- (已弃用)静态数据成员的命名空间作用域声明在类中使用constexpr说明符定义
struct S {
static constexpr int x = 42; // implicitly inline, defines S::x
};
constexpr int S::x; // declares S::x, not a redefinition
因此,从C++17开始,我将使用比枚举更具表达性的静态constexpr定义。
- 我应该在 C++ 中何时/为什么使用 STATIC?
- 为什么我的编译器无法弄清楚这种转换,它何时存在?
- c++ 何时/为什么由值构造/销毁捕获变量
- 为什么或何时应在调用之前将可调用函数参数强制转换为右值?
- 何时以及为什么我们必须使用malloc?
- 物理和标准::<double>numeric_limits::epsilon()...我们何时以及为什么需要它?
- 三元运算符为什么以及何时返回左值?
- 为什么或何时用指针声明int
- 引用变量何时合适,为什么?你能解释一下实际的语法和位置吗?
- 静态constexpr int与老式枚举:何时以及为什么
- 何时以及为什么在堆C++上声明成员变量
- 为什么/何时使用类数据成员而不定义该类的对象
- 为什么需要它!=obj.end() 尽管每个迭代器都应该知道自己何时终止
- 我何时以及为什么要使用 -fno-elide-constructors
- 为什么以及何时需要使用 #pragma
- 为什么以及何时应该使用IPC_NEW来创建信号量
- 为什么以及何时删除复制构造函数和运算符=
- 堆叠在C++.为什么以及何时使用它们
- 何时以及为什么要在constexpr中使用static
- 何时/为什么(< 0)可能在表达式中分支?