How to declare constexpr extern?
Is it possible to declare a variable extern constexpr
and define it in another file?
I tried it but the compiler gives error:
Declaration of
constexpr
variable 'i
' is not a definition
in .h:
extern constexpr int i;
in .cpp:
constexpr int i = 10;
Solution 1:
no you can't do it, here's what the standard says (section 7.1.5):
1 The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. Function parameters cannot be declared constexpr. — end note ]
some examples given by the standard:
constexpr void square(int &x); // OK: declaration
constexpr int bufsz = 1024; // OK: definition
constexpr struct pixel { // error: pixel is a type
int x;
int y;
constexpr pixel(int); // OK: declaration
};
extern constexpr int memsz; // error: not a definition
Solution 2:
C++17 inline
variables
This awesome C++17 feature allow us to:
- conveniently use just a single memory address for each constant
- store it as a
constexpr
- do it in a single line from one header
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 do inline variables work?
Tested in GCC 7.4.0, Ubuntu 18.04.
Solution 3:
No. Extern constexpr does not make any sense. Please read http://en.cppreference.com/w/cpp/language/constexpr
i.e. the bit " it must be immediately constructed or assigned a value. "
Solution 4:
What you probably want is extern and constexpr initialization, e.g.:
// in header
extern const int g_n;
// in cpp
constexpr int g_n = 2;
This is support though in Visual Studio 2017 only through conformance mode:
- /Zc:externConstexpr (Enable extern constexpr variables)
- constexpr definition of extern const variable
Solution 5:
I agree with 'swang' above, but there is a consequence. Consider:
ExternHeader.hpp
extern int e; // Must be extern and defined in .cpp otherwise it is a duplicate symbol.
ExternHeader.cpp
#include "ExternHeader.hpp"
int e = 0;
ConstexprHeader.hpp
int constexpr c = 0; // Must be defined in header since constexpr must be initialized.
Include1.hpp
void print1();
Include1.cpp
#include "Include1.hpp"
#include "ExternHeader.hpp"
#include "ConstexprHeader.hpp"
#include <iostream>
void print1() {
std::cout << "1: extern = " << &e << ", constexpr = " << &c << "\n";
}
Include2.hpp
void print2();
Include2.cpp
#include "Include2.hpp"
#include "ExternHeader.hpp"
#include "ConstexprHeader.hpp"
#include <iostream>
void print2() {
std::cout << "2: extern = " << &e << ", constexpr = " << &c << "\n";
}
main.cpp
#include <iostream>
#include "Include1.hpp"
#include "Include2.hpp"
int main(int argc, const char * argv[]) {
print1();
print2();
return 0;
}
Which prints:
1: extern = 0x1000020a8, constexpr = 0x100001ed0
2: extern = 0x1000020a8, constexpr = 0x100001ed4
IE the constexpr
is allocated twice whereas the extern
is allocated once.
This is counterintuitive to me, since I 'expect' constexpr
to be more optimized than extern
.
Edit: const
and constexpr
have the same behaviour, with regard to allocation, therefore from that point of view the behaviour is as expected. Though, as I said, I was surprised when I came across the behaviour of constexpr
.