简体   繁体   中英

Why should I prefer static constexpr int in a class over enum for class-level integral constants?

C++17 Update: static constexpr variables are implicitly inline so there's no external definition necessary.


Original question:

Let's say I have a list of constants such as

struct Cls {
    static constexpr int N = 32;
    static constexpr int M = 64;
};

This of course suggests that I add definitions for these to avoid ODR-usage issues that may occur so I need:

constexpr int Cls::N;
constexpr int Cls::M;

Why should I prefer this over

struct Cls {
    enum : int {
        N = 32,
        M = 64
    };
};

Which saves me of the ODR-usage headaches since N and M are more truly just constants and not objects in their own right (a bigger deal if this is header-only) and is shorter. I could explicitly specify the type enum : long long or whatever if need be. What is the advantage of the first?

One difference is that you can take the address of a static constexpr but not of an enum .

Another is that constexpr isn't supported by older versions of the language (it was introduced in C++11).

I'd use enum only if the values belong together. I'd also give the enum a name that describes that relationship. I wouldn't use an enum for defining unrelated constants.

Perhaps no advantage for your usage because you're just using simple fixed integer values.

But, [AFAIK] constexpr can be more general as it allows initialization from anything that can be evaluated at compile time.

From type_traits :

 /// integral_constant template<typename _Tp, _Tp __v> struct integral_constant { static constexpr _Tp value = __v; typedef _Tp value_type; typedef integral_constant<_Tp, __v> type; constexpr operator value_type() const { return value; } #if __cplusplus > 201103L #define __cpp_lib_integral_constant_callable 201304 constexpr value_type operator()() const { return value; } #endif };

Thus, constexpr has usage in metaprogramming.

The following is a bit rough.

If you had a function like:

constexpr unsigned
bitmask(int bitno)
{

    return 1u << bitno;
}

You might find a usage such as:

constexpr unsigned BIT_0 = bitmask(0);
constexpr unsigned BIT_1 = bitmask(1);

The reason I would give you is that using enum { } for constants is a misuse of the term enum . You're not enumerating anything. It's a common misuse, granted; it has its practical advantages; but it's just kind of wrong. There should be a way to say "this is just a compile-time constant and nothing else". constexpr isn't that thing either, but it's closer than enum. And it's rightly the case that you can't enum floating-point values.

That being said - I often use enums for constants myself, when I want to protect myself against people writing something like void* ptr = &some_constant_value; std::cout << ptr; void* ptr = &some_constant_value; std::cout << ptr;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM