32

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?

3
  • 1
    If you were using something other than integrals then you'd HAVE to use constexpr. Commented May 16, 2016 at 17:48
  • 1
    @CrazyEddie This is quite obvious!! The example is not there for a mistake. :-) Commented May 16, 2016 at 18:17
  • 1
    I'd like to know the reasons of the downvoters and the ones that voted to close the question as primarily opinion-based, with such a technical answer posted by @SergeyA (as correctly pointed out in the question for its technical parts, indeed). Quite funny. Commented May 16, 2016 at 19:18

3 Answers 3

31

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.

Sign up to request clarification or add additional context in comments.

22 Comments

You can work around the issue by instantiating i in a cpp file, but then that allows you to take the address of &. Taking the address of a value is nonsensical. Enums don't allow this.
@BenCollins You can see 3.2p3 or any other site like this one.
@gnzlbg inline variable? Never heard about them. Have you a link? Thanks.
@skypjack N4424
@SergeyA, "If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program."
|
5

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.

6 Comments

It makes sense and I suspect almost the same. I've asked on SO in regard of obscure technical reasons for I'm not sure about that, that's all. :-)
@skypjack let's hope someone has more insight.
There is a benefit in using enum, see my answer.
@RSahu, I doubt there is a case when someone wants to ODR-use a constexpr. This is usually undesired.
@SergeyA, I am not willing to bet against that. I have seen developers pull some crazy stuff.
|
5

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.