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
In C++17
you are clear. In C++11
, you can wrap it in a function:
constexpr double PI () { return 3.14; }
I can have a definition like this in a header file?
Yes
Is there any problem in having this in a header file that would be included to several cpp files?
No
A constexpr
variable (int, double, etc) in do not occupy memory, thus it does not have memory address and compiler handles it like #define
, it replaces variables with value. This is not true for objects though, that is completely different. Read this:
To elaborate on comments made. To avoid overhead, in most cases constexpr
is replaced with its value, but in cases where you have to get an address of constexpr
compiler does allocate memory each time. So if you have ab.h
which contains:
constexpr double PI = 3.14;
and you have a.cpp
which contains:
std::cout << PI << "\n";
PI would be replaced no memory would be allocated.
Where as if you have b.cpp
:
double *MY_PI = &PI;
memory would be allocated specifically for that instance (or maybe for entire b.cpp
file).
EDIT: Thanks to @HolyBlackCat and his code he had in comments bellow it seems that memory is allocated per file.
EDIT 2:
it is file based. So I have constExpr.h
containing follwoing:
#ifndef CONSTEXPR_H
#define CONSTEXPR_H
#include <iostream>
constexpr int a = 5;
void bb ();
void cc ();
#endif
a.cpp
containing follwing:
#include <iostream>
#include "constExpr.h"
void aa () {
std::cout << &a << "\n";
}
int main () {
aa ();
bb ();
cc ();
return 0;
}
and b.cpp
containing following:
#include "constExpr.h"
void bb () {
std::cout << &a << "\n";
}
void cc () {
std::cout << &a << "\n";
}
output is:
0x400930
0x400928
0x400928
CONCLUSION
But, honestly I would never do something like I did in my examples. This was a great challenge for me and my brain. constexpr
is added mostly to replace #define
. As we know #define
is hard to debug due to the fact that compiler cannot check #define
statements for error. So unless you do something like above it is just like #define
except it is handled at compile time not by precomiler.
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
GitHub upstream.
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++
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
.