Why should I prefer static constexpr int in a clas

2019-03-25 11:15发布

问题:

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?

回答1:

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.



回答2:

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


回答3:

I'm pretty sure I'm going to get flamed for this, but...

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;