42

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" 
7
  • 4
    There's no difference. constexpr implies const. const implies static. Commented Aug 31, 2017 at 18:04
  • 1
    None: constexpr on variables implies const, and const integral variables at namespace scope have internal linkage by default. Commented Aug 31, 2017 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. Commented Aug 31, 2017 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? Commented Aug 31, 2017 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. Commented Aug 31, 2017 at 18:14

3 Answers 3

38

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

Sign up to request clarification or add additional context in comments.

9 Comments

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.
@DanielH: Yes, possibly, as long as you don't odr-use the variable.
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.
@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; }.
@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.
|
6

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.

Comments

4

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

1 Comment

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.