use of constexpr in header file
Solution 1
constexpr
implies const
and const
on global/namespace scope implies static
(internal linkage), which means that every translation unit including this header gets its own copy of PI
. The memory for that static is only going to be allocated if an address or reference to it is taken, and the address is going to be different in each translation unit.
That implied static
for const
variables was introduced specifically to use const
instead of #define
in header files in C++ to define constants. Without static
there would be multiple symbol definitions linker error if that header file is included in more than one translation unit which were linked together.
In C++17 you can also make it inline
, so that there is only ever a single copy of PI
if an address or reference to it is taken (i.e. not static
). inline
variables were introduced in C++17 to allow for header-only libraries with non-const variable definitions in the header files. constexpr
on static data members implies inline
, so inline
is unnecessary there.
In other words, you should use constexpr
for your constants in header files, if possible, otherwise const
. And if you require the address of that constant to be the same everywhere mark it as inline
.
Solution 2
In C++17
you are clear. In C++11
, you can wrap it in a function:
constexpr double PI () { return 3.14; }
Solution 3
C++17 inline
variable runnable example
C++17 inline variables were mentioned at: use of constexpr in header file and here is a minimal runnable example that shows that only a single memory location is used:
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_i;
}
Compile and run:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
The C++ standard guarantees that the addresses will be the same. C++17 N4659 standard draft 10.1.6 "The inline specifier":
6 An inline function or variable with external linkage shall have the same address in all translation units.
cppreference https://en.cppreference.com/w/cpp/language/inline explains that if static
is not given, then it has external linkage.
See also: How to declare constexpr extern?
Tested in GCC 7.4.0, Ubuntu 18.04.
C++20 std::math::pi
Note that for the specific case of Pi, C++20 offers a dedicated variable template as shown at: How to use the PI constant in C++
mans
Updated on July 09, 2022Comments
-
mans almost 2 years
I can have a definition like this in a header file?
constexpr double PI=3.14;
Is there any problem in having this in a header file that would be included to several cpp files?
I am worried that since it says in standard that this constexpr has its own memory, putting it in header, and adding header to several cpp files, generate multiple copies of the same value in memory and some other nasty problems.
I am using C++11
-
Joseph D. almost 6 yearswould there be an ODR issue when it's not
inline
? -
johnco3 over 5 years@MaximEgorushkin I'm a little confused with the global namespace thing, so if I have a header file foo.h with #pragma once and I have a constexpr auto gFOO = 768; in that header, In order for there to be only 1 address of this variable shared across all translation units - do I need to prefix it with inline for c++17?
-
Maxim Egorushkin over 5 years@johnco3 Correct,
inline
is needed for that. -
wardw over 5 years@MaximEgorushkin For C++17, if you do make
PI
bothconstexpr
andinline
does this require a single copy? i.e. if no address is ever taken in any translation unit, that the object may never be created (but if an address is taken, we're guaranteed only one copy)? -
Maxim Egorushkin over 5 years@wardw Only
inline
variables with external linkage must have the same address.constexpr
implies internal linkage. -
wardw over 5 yearsTo correct my own comment, I now realise
constexpr
is implicitlyinline
(and at global/namespace scope, sinceconst
, implicitlystatic
). But at some point i'd be interested to know wether a static variable must require static storage even if it's never used in a runtime context, e.g. astatic constexpr
that's only used as a value at compile-time. In other-words, wether the optimiser is able to elide the static storage (?) -
Ciro Santilli OurBigBook.com over 4 yearsNote that in C++
constexpr
withoutinline
still generates multiple addresses and takes up multiple memory locations which is not ideal, this can be easily tested by modifying the example I provided to removeinline
: stackoverflow.com/a/57399173/895245 -
KevinZ over 4 yearsYou are right about variables, but
constexpr
does implyinline
for functions. For variables, not havinginline
can further risk ODR if included from multiple translation units. -
Maxim Egorushkin almost 4 years@wardw Run
nm --demangle --defined-only <xyz.o> | fgrep <part-of-symbol-name>
and see whether the symbol exists and has non-0 address. If it exists and has non-0 address that means there is a non-inline version of it. May be-Winline
could warn about that, but I haven't tried.