54

Can I 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

mloskot
  • 37,086
  • 11
  • 109
  • 136
mans
  • 17,104
  • 45
  • 172
  • 321
  • 2
    What's the issue with the `constexpr` definition (apart from the typo)? – UnholySheep May 23 '18 at 12:48
  • 3
    @UnholySheep I am asking the question! Is there any problem? – mans May 23 '18 at 12:49
  • 3
    I don't understand - did you get a compiler error or what exactly is the problem? I assume you tried using it and had some issue – UnholySheep May 23 '18 at 12:51
  • @UnholySheep I am worrioed 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. – mans May 23 '18 at 12:53
  • 5
    Okay - there's the basis of a good question there but you should add that kind of detail into your question's body so people know what it is you are really asking about :) – Lightness Races in Orbit May 23 '18 at 12:54
  • 1
    @MaxVollmer Done! – mans May 23 '18 at 12:57
  • Presumably you want to do this for more than just 'pi'. If not, you can get 'pi' also [this way](https://stackoverflow.com/a/1727896/2646505) – Tom de Geus May 23 '18 at 12:59

4 Answers4

87

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 3
    would there be an ODR issue when it's not `inline`? – Joseph D. May 23 '18 at 13:07
  • 1
    @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? – johnco3 Aug 09 '18 at 16:42
  • 1
    @johnco3 Correct, `inline` is needed for that. – Maxim Egorushkin Aug 09 '18 at 16:52
  • @MaximEgorushkin For C++17, if you do make `PI` both `constexpr` and `inline` 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)? – wardw Oct 29 '18 at 13:58
  • 1
    @wardw Only `inline` variables with external linkage must have the same address. `constexpr` implies internal linkage. – Maxim Egorushkin Oct 29 '18 at 14:55
  • To correct my own comment, I now realise `constexpr` is implicitly `inline` (and at global/namespace scope, since `const`, implicitly `static`). 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. a `static constexpr` that's only used as a _value_ at compile-time. In other-words, wether the optimiser is able to elide the static storage (?) – wardw Dec 23 '18 at 15:00
  • @wardw Run `nm --demangle --defined-only | fgrep ` 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. – Maxim Egorushkin Jul 08 '20 at 18:23
12

In C++17 you are clear. In C++11, you can wrap it in a function:

constexpr double PI () { return 3.14; }
KevinZ
  • 3,036
  • 1
  • 18
  • 26
  • 1
    Note that in C++ `constexpr` without `inline` 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 remove `inline`: https://stackoverflow.com/a/57399173/895245 – Ciro Santilli OurBigBook.com Aug 07 '19 at 17:00
  • You are right about variables, but `constexpr` does imply `inline` for functions. For variables, not having `inline` can further risk ODR if included from multiple translation units. – KevinZ Aug 14 '19 at 01:06
8

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(&notmain_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 &notmain_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++

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
1

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.