12

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?

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • As written, `N` and `M` are of an anonymous `enum` type, not of type `int`. As you say, you can specify `enum : int`; I suggest you should do so in your example. – Keith Thompson Feb 04 '16 at 22:44
  • @zenith the question in that is "_Isn't there some way to tell the compiler that constexpr int SOME_VALUE=27 means that SOME_VALUE should be treated only as a compile time constant and never an object with external linkage_" and that is what the answers address. I am asking if there is any advantage to using `static constexpr` instead of `enum`s that would motivate me to use them for class-level constants. – Ryan Haining Feb 04 '16 at 22:53
  • @RyanHaining: "*Why should I prefer this over*" It's not clear what you mean. It's not like `constexpr` was added to the language as a way to keep people from using `enum`s for constant values or something. So why do you think you should want to prefer it over the `enum` method? – Nicol Bolas Feb 04 '16 at 23:02
  • Related: [static const Member Value vs. Member enum : Which Method is Better & Why?](http://stackoverflow.com/q/204983/3425536) – Emil Laine Feb 04 '16 at 23:03
  • 4
    Uniformity with constexpr values which cannot be inside `enum`, as `float`/`double`. – Jarod42 Feb 04 '16 at 23:03
  • In addition to what you've shown, at least one compiler (MS VC++) can show the actual name of an enum while debugging rather than just the numeric value. – Mark Ransom Feb 04 '16 at 23:03
  • 2
    I had strong doubts that one could take the address of a `constexpr` value of built-in type, i.e. that it was an object, necessitating the definitions in addition to declarations. Checking the standard I found I was wrong, and Ryan's premise correct re formal requirements (and g++ enforces this, while MSVC does not for the example at hand). Summing up, except for the possibility of providing the value in the declaration rather than in the definition also for non-intergral types, a `constexpr` static data member is no different from a plain `const` static data member. – Cheers and hth. - Alf Feb 05 '16 at 00:38
  • Personally I'd prefer the `enum` version – M.M Feb 05 '16 at 01:40
  • 1
    Check isocpp [here](https://isocpp.org/blog/2016/05/quick-q-static-constexpr-int-vs-old-fashioned-enum-when-and-why), which concludes that enum is a better way. – KeyC0de Sep 30 '18 at 07:40

3 Answers3

5

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.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • Well, yes, but a good compiler will eliminate the memory-read and use immediate-mode addressing --- unless you take the constant's address in which case you force it to have a physical memory address. – Paul J. Lucas Feb 04 '16 at 23:22
  • 2
    @PaulJ.Lucas I think he is saying that the `static constexpr` ver is the only option if you specifically do want to take the address of the constant – M.M Feb 05 '16 at 01:40
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);
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • your last example only works if `bitmask` is declared as `constexpr`. I am aware of the various uses of `constexpr` unrelated to what I'm asking. – Ryan Haining Feb 05 '16 at 00:03
  • I did say "a bit rough" [but I'll fix]. But, if you know about the other `constexpr` uses, didn't you already answer your own question? (i.e.) You delineated the issues (re. ODR) and that you can set the `enum` type. Use `enum` [because it's simpler] unless you can't because the rhs is too complex for `enum` [as in `bitmask`] – Craig Estey Feb 05 '16 at 00:22
  • 1
    The question is about why I would want to replace an `enum` with a `constexpr` for an integer constant in a class. If there really is no advantage (besides it having an address) that's cool too. – Ryan Haining Feb 05 '16 at 00:24
  • Re `#if __cplusplus > 201103L`, the code has *already* used the `constexpr` keyword introduced in C++11. – Cheers and hth. - Alf Feb 05 '16 at 00:33
  • As I said in my first sentence, I don't think there is an advantage for the use case you provided. You could define `enum foo : int ...` and then `static enum foo val;` and take the address of `val` [or use `enum foo *ptr`], so you can take addresses of and use pointers to `enum` – Craig Estey Feb 05 '16 at 00:33
  • @Cheersandhth.-Alf I just lifted the code wholesale from `/usr/include/c++/5.3.1/type_traits` merely as an example of `constexpr` usage [written by those wiser than me] – Craig Estey Feb 05 '16 at 00:38
  • @CraigEstey: We've just proved they were not wiser than you. ;-) Well it might be an artifact of maintenance. Something that was written in C++03 days and just was not removed, or maybe they once used a dangerous workaround with `constexpr` as a macro. – Cheers and hth. - Alf Feb 05 '16 at 00:40
  • I believe this: `unsigned BIT_0 = bitmask(0)` is OK under GCC,ICC,MSVC but needs a template under Clang: `unsigned BIT_0 = bitmask<0>;`. GCC,ICC,MSVC will propagate the constant as expected; Clang feels the value of `0` can somehow change after the file is saved. – jww Nov 12 '16 at 17:35
2

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;

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
einpoklum
  • 118,144
  • 57
  • 340
  • 684