静态constexpr int与老式枚举:何时以及为什么

Static constexpr int vs old-fashioned enum: when and why?

本文关键字:何时 为什么 枚举 constexpr int 静态      更新时间:2023-10-16

这可能是一个基本问题,但我现在自己看不到答案。

考虑以下代码:

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这样的结构用于相同的目的(主要是特征定义)。

据我所见,它们都是在编译时解决的,而且它们几乎解决了相同的问题,但在我看来,TU可读性强得多(好吧,我知道,这是我的个人观点)。

我的问题很简单:一种解决方案比另一种更好,有什么技术原因吗
更重要的是,有没有哪一种情况不是可行的解决方案?

请注意,以下答案不适用于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定义。