静态constexpr int与老式枚举:何时以及何时?

时间:2016-05-16 17:32:22

标签: c++ c++11 enums constexpr

这可能是一个基本问题,但我现在无法看到自己的反应。

请考虑以下代码:

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更具可读性(好吧,我知道,我的个人意见。)

我的问题非常简单:是否有任何技术原因可以解决一个解决方案比另一个解决方案更好的问题? 更有甚者,其中一个不是一个可行的解决方案吗?

3 个答案:

答案 0 :(得分:25)

使用时,积分常数没有明显的差异。

但是,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):undefined   引用“T::icollect2:错误:ld返回1退出状态

然而,真正的enum始终有效。

答案 1 :(得分:4)

我怀疑它的遗留代码。

enum { value = (b ? 42 : 0) };

是C ++ 03以及C ++ 11中的有效代码。

static constexpr int value = (b ? 42 : 0);

仅在C ++ 11中有效。

  

更重要的是,是否存在其中一个不可行的解决方案?

两者都是C ++ 11中可行的解决方案。选择使用哪一个取决于团队。这将是一个政策决定的问题。

正如answer by SergeyA所示,enum是真常数。你不能ODR使用它们。您可以ODR使用constexpr。根据您的应用需要哪些,您可以决定是使用enum还是constexpr

答案 2 :(得分:0)

currently accepted answer by SergeyA从C ++ 17(Definitions and 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定义。