Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This is maybe a basic question, but I cannot see the response by myself right now.

Consider the following code:

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, "!");
}

I'm used to using structs like T, but more than once I've seen structs like U used for the same purpose (mostly traits definition).

As far as I can see, they are both resolved at compile time and they solve almost the same problem, but it seems to me that T is far more readable than U (well, I know, my personal opinion).

My question is pretty simple: is there any technical reason for which one solution is better than the other one?
Even more, is there any case for which one of them is not a viable solution?

like image 203
skypjack Avatar asked May 16 '16 17:05

skypjack


People also ask

Why does constexpr need to be static?

A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.

Are enums constexpr?

As the answer by SergeyA indicates, enum are true constants.

Should I use constexpr or const?

const applies for variables, and prevents them from being modified in your code. constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.

Can a constexpr be static?

It may contain local variable declarations, but the variable must be initialized. It must be a literal type, and can't be static or thread-local. The locally declared variable isn't required to be const , and may mutate. A constexpr non- static member function isn't required to be implicitly const .


3 Answers

Please note, answer below is not applicable for C++ 17 and later.

There will be no noticeable difference for integral constants when used like this.

However, enum is actually better, because it is a true named constant. constexpr integral constant is an object which can be, for example, ODR-used - and that would result in linking errors.

#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);
}

When check(T::i) is uncommented, the program can not be linked:

/tmp/ccZoETx7.o: In function `main': ccc.cpp:(.text+0x45): undefined reference to `T::i' collect2: error: ld returned 1 exit status

However, the true enum always works.

like image 59
SergeyA Avatar answered Oct 08 '22 19:10

SergeyA


I suspect it's legacy code.

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

is valid code in C++03 as well as C++11.

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

is valid only in C++11.

Even more, is there any case for which one of them is not a viable solution?

Both are viable solutions in C++11. The choice of which one to use depends on a team. It's going to be a matter of a policy decision.

As the answer by SergeyA indicates, enum are true constants. You cannot ODR-use them. You can ODR-use a constexpr. Depending on which of these is desirable for your application, you can decide whether to use enums or constexprs.

like image 40
R Sahu Avatar answered Oct 08 '22 18:10

R Sahu


The currently accepted answer by SergeyA no longer holds as of C++17 (Definitions and ODR).

Every declaration is a definition, except for the following:

  • ...
  • (deprecated) Namespace scope declaration of a static data member that was defined within the class with the constexpr specifier
struct S {
    static constexpr int x = 42; // implicitly inline, defines S::x
};
constexpr int S::x; // declares S::x, not a redefinition

Hence, as of C++17, I would use the static constexpr definition which is more expressive than the enum.

like image 4
Touloudou Avatar answered Oct 08 '22 19:10

Touloudou