80

With C++17 we get inline variables.

One of the uses for them is to define constant fields in classes.

So what's the difference between these two constant definitions:

class MyClass {
    static constexpr int myFirstVar = 10;
    static const inline int mySecondVar = 100;
};

Of course constexpr makes myFirstVar implicitly inline.

What's the better choice here, to use constexpr or inline?

Note: when you don't need constness, then inline makes it easier. With constexpr you don't have that choice.

Ajay
  • 18,086
  • 12
  • 59
  • 105
fen
  • 9,835
  • 5
  • 34
  • 57

1 Answers1

77

You don't have to specify an initializer for mySecondVar at the point of declaration. Nor is the initializer required to be constexpr itself.

This means that if we attempt to define myFirstVar like this:

class MyClass {
    static constexpr int myFirstVar;
};

int MyClass::myFirstVar = 1;

Or like this:

#include <cstdlib>

class MyClass {
    static constexpr int myFirstVar = rand();
};

It's ill-formed either way. constexpr semantics demand it and for a good reason.

The inline specifier approach allows us to include a static variable definition in the header itself, without the initializer being constexpr; or if the initializer is fairly complex it doesn't have to be in the class definition itself.

So this is a perfectly valid header in C++17:

#include <cstdlib>

class MyClass {
    static const int mySecondVar;
};

inline const int MyClass::mySecondVar = rand();

The standard promises us that all translation units that include the header will see the same value for the variable, even though we won't know what it is until run-time.

It's mostly a library writers tool. Assume your library is header only. Then in the olden days, what were your options if you needed a static constant defined like this?

Well, you could have an object file shipped with your library. It will be compiled from a translation unit that contains just the constant definition. Now the library isn't header-only.

Or you could rely on inline functions instead. The inline variable effect can be achieved with the following:

class MyClass {
    static inline int mySecondVar();
};

inline int MyClass::mySecondVar() {
  static const int value = rand();
  return value;
}

But it's hidden behind a wall of syntax, and masks what is essentially a constant, with a function call operator.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Does it mean that inline variable with static storage duration are insensitive to initialization order? – Oliv Jul 19 '17 at 08:20
  • @Oliv - SIOF still applies if you aren't careful. The only thing guaranteed by `inline` variables is that there will be only *one* variable that is initialized **once**. – StoryTeller - Unslander Monica Jul 19 '17 at 08:28
  • Actually I asked it because your last code exemple implies there will not be any SIOF. So inline variable are not "equivalent" to this exemple as I understand the word "equivalent". Maybe you could mention it in the answer. – Oliv Jul 19 '17 at 08:41
  • @Oliv - By "equivalent" I mean achieving the same effect. Reworded. – StoryTeller - Unslander Monica Jul 19 '17 at 08:44
  • @Oliv - Actually, my last code sample didn't imply that come to think of it. That function can be called from an initialzier for another static object. If `value` referred to an extern variable instead, it would cause a SIOF as well. – StoryTeller - Unslander Monica Jul 19 '17 at 08:52
  • Don't worry, this is the old problem of images symbols or exemples, there is always some poeple to see more in it than the intent of the author! Artist loves it. But for teaching this is always a problem ! I admit also i prefer your previous wording. – Oliv Jul 19 '17 at 17:16
  • Are you sure about this? I don’t think defining an `inline` variable outside the class is valid in C++17 unless it’s `constexpr`, in which case it’s still deprecated and can’t have an initialization. – Daniel H Jul 19 '17 at 17:48
  • 1
    @DanielH - Quite sure. The regular semantics of non-const statics still apply. The inline specifier only makes sure the multiple identical definitions keep the whole thing ODR-correct. – StoryTeller - Unslander Monica Jul 19 '17 at 17:58
  • 1
    @StoryTeller The part I was getting confused about was “An inline static data member may be defined in the class definition and may specify a *brace-or-equal-initializer*.” ([class.static.data]p3). I thought that first “may” was a “must”. – Daniel H Jul 19 '17 at 18:11
  • Hi (expert). Sorry to high-jack you from this answer, but possibly (seeing as this answer touches the same subject, and that you often post very deep-dwelling answers) you can bring some insights to the OP's questions relating my answer of [this Q&A](https://stackoverflow.com/a/46742542/4573247); I've exhausted my own knowledge on the subject by now, and I believe the outstanding question is of the form _"Why did the Standard take this decision"_, something I lack knowledge to speculate in. – dfrib Oct 16 '17 at 12:11
  • 2
    ... Particularly, why the described modification _"The `constexpr` specifier implies `inline` for variables as well as functions"_ from [P0386R0](http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf), specified to be applied onto (7.1.5p1) as _"A function **or variable declared with the constexpr specifier** is implicitly an `inline` function or variable ..."_, ended up as limited to the subset of variables that are static data members; [dcl.constexpr]/1: _"A function or **static data member declared with the constexpr specifier** is implicitly an inline function or variable ..."_. – dfrib Oct 16 '17 at 12:11
  • Note that a definition is only needed for a `static constexpr` class member in case that member is ODR-used. Simply put: if the variable is only ever used by value, it is unnecessary to define it. – Arne Vogel Aug 19 '19 at 12:10
  • One thing to consider is that static constexpr will required and perform the computation needed (if any) at compile time, when the other one will perform it at runtime... – sandwood Sep 23 '21 at 18:56