39

In the C++11 standard, what is the difference between constexpr and static constexpr global variables when defined in a header? More specifically, when multiple translation units include the same header, which declaration (if any) is guaranteed to define the same variable across the translation units?

e.g.,

cexpr.h:

#ifndef CEXPR_H
#define CEXPR_H

constexpr int cint = 1;
static constexpr int scint = 1;

#endif

a.cpp:

#include "cexpr.h"

b.cpp:

#include "cexpr.h"
Danra
  • 9,546
  • 5
  • 59
  • 117
  • 2
    There's no difference. `constexpr` implies `const`. `const` implies `static`. – cpplearner Aug 31 '17 at 18:04
  • None: `constexpr` on variables implies `const`, and const integral variables at namespace scope have internal linkage by default. – Kerrek SB Aug 31 '17 at 18:04
  • 8
    @cpplearner: Nit: I wouldn't quite say "const implies static", because `extern const int` is valid, but `extern static const int` is not -- so the `static` is not as much "implied" as it is a "default" of sorts. – Kerrek SB Aug 31 '17 at 18:05
  • @KerrekSB To make sure I understand, neither `constexpr` nor `static constexpr` would allow me to get the same object across separate translation units? – Danra Aug 31 '17 at 18:08
  • 2
    @Danra: In C++14, no, you'd have to use `extern const int` instead (which is still suitable as a constant expression in the TU that defines it, since you need an initializer to be usable as a constant expression). In C++17 you would use `inline constexpr int a = 10;` to get a single object. – Kerrek SB Aug 31 '17 at 18:14
  • @KerrekSB Great. If you'd like to post that as answer I'll accept it. Thanks also for helping me better understand the purpose of C++17's inline variables. – Danra Aug 31 '17 at 18:17
  • @Danra: Sure, done. – Kerrek SB Aug 31 '17 at 21:05

3 Answers3

31

In your current example there is no difference: On variable declarations, constexpr implies const, and a const variable at namespace scope has internal linkage by default (so adding static does not change anything).

In C++14, you cannot declare a variable as constexpr and have it have external linkage unless you only ever do this in one single translation unit. The reason is that constexpr variables require an initializer, and a declaration with initializer is a definition, and you must only have a single definition.

However, what you can do is use a normal integral constant, which you can declare (not define) as extern, and in the translation unit where it is defined it can even be used as a constant expression:

lib.h:

extern const int a;

lib.cpp:

#include "lib.h"

const int a = 10;

int b[a] = {1, 2, 3};   // OK in this translation unit

In C++17, there is a new feature "inline variables" which lets you say:

inline constexpr int a = 10;

And this is an "inline definition" that can appear repeatedly, and each definition defines the same entity (just like all the other "inline" entities in the language).

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    It is possible to have a `constexpr` definition in a header file before C++17, but you had to use various workarounds like having an `inline constexpr` *function* with a `constexpr static` variable inside it. – Daniel H Aug 31 '17 at 21:13
  • @DanielH: Yes, possibly, as long as you don't odr-use the variable. – Kerrek SB Aug 31 '17 at 21:29
  • 1
    What I meant was, in a header file, `inline constexpr int i() {static constexpr int value = 17; return value;}` should work, and then you can say `i()`. It’s weird to use function call syntax, but I think it works. – Daniel H Aug 31 '17 at 21:43
  • @DanielH: I understand that. What *I* meant was that you should be careful not to inadvertently say something like `inline constexpr const Foo& f() { static constexpr Foo x; return x; }`. It appears to work, but is an odr-violation. – Kerrek SB Aug 31 '17 at 22:00
  • Wait, really? That still looks valid to me; can you link to documentation on that? – Daniel H Aug 31 '17 at 22:02
  • @KerrekSB Joining Daniel's question :) Why is it an ODR violation? – Danra Aug 31 '17 at 22:14
  • 1
    @DanielH: No, sorry, you're right, that one is fine. I got this confused. An example that wouldn't be fine is `static constexpr int a = 10; inline constexpr const int& f() { return a; }`. – Kerrek SB Aug 31 '17 at 23:30
  • 1
    @KerrekSB Yeah, you need the `static` variable to be *inside* the function. Also not fine would be most attempts to get rid of the function call syntax, like `inline constexpr const Foo& f() { static constexpr Foo x; return x; } constexpr Foo& x = f();`, unfortunately. I think you *can* do it if you can let it be a class member, though I don’t remember how and it involves some convoluted redirection. – Daniel H Aug 31 '17 at 23:34
4

I think this article explains more clear. 6.8 — Global constants and inline variables

Because const globals have internal linkage, each .cpp file gets an independent version of the global variable that the linker can’t see. In most cases, because these are const, the compiler will simply optimize the variables away.
The term “optimizing away” refers to any process where the compiler optimizes the performance of your program by removing things in a way that doesn’t affect the output of your program. For example, lets say you have some const variable x that’s initialized to value 4. Wherever your code references variable x, the compiler can just replace x with 4 (since x is const, we know it won’t ever change to a different value) and avoid having to create and initialize a variable altogether.

So, the "cint " and "scint" are all internal linkage variables.

The best practice to define global variable after C++ 17:

inline constexpr double pi = 0;

Working Mechanism:

C++17 introduced a new concept called inline variables. In C++, the term inline has evolved to mean “multiple definitions are allowed”. Thus, an inline variable is one that is allowed to be defined in multiple files without violating the one definition rule. Inline global variables have external linkage by default.

Inline variables have two primary restrictions that must be obeyed: 1) All definitions of the inline variable must be identical (otherwise, undefined behavior will result). 2) The inline variable definition (not a forward declaration) must be present in any file that uses the variable.

The compiler will consolidate all inline definitions into a single variable definition. This allows us to define variables in a header file and have them treated as if there was only one definition in a .cpp file somewhere. These variables also retain their constexpr-ness in all files in which they are included.

IclodQ
  • 101
  • 4
2

If you can, prefer the static constexpr because with the constexpr it depends on the toolchain how likely it will get done on compile-time. Gcc is most aggressive, MSVS least aggressive and clang is in between.

Instead of leaving some values up to optimizer to decide it will do it at compile-time be more explicit and force it.

Reference:

https://www.youtube.com/watch?v=4pKtPWcl1Go

Anton Krug
  • 1,555
  • 2
  • 19
  • 32
  • 1
    Thanks, probably good advice to follow in general toi avoid conusion, though for global variables it makes no difference (@Kerrek SB explains why in the first paragraph of his answer). – Danra Jun 04 '22 at 00:41