I want to confirm that this code is legal (or not legal?) C++17.
#include <iostream>
template<int N> inline constexpr float MyConst;
template<> inline constexpr float MyConst<1> = 1.1f;
template<> inline constexpr float MyConst<2> = 2.2f;
int main ()
{
std::cout << MyConst<1> << '\n';
return 0;
}
I don't get errors (and get correct output) if compiled by g++ and MSVC,
but Intel and clang give an error:
test.cpp(3): error: missing initializer for constexpr variable
template<int N> inline constexpr float MyConst;
^
Compiled with -std=c++17
(/std:c++17
for MSVC).
Tried with latest compilers on godbolt as well as on my local machine.
constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.
The constexpr keyword implies inline. Performing computations at compiletime can have many advantages over doing them at runtime. Firstly it can increase runtime performance, as computations that would be done at runtime are already executed at compiletime.
constexpr int a = 2; Static specifies the lifetime of the variable. A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.
Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.
A constexpr
variable must be immediately initialised. Hence the template for MyConst
needs an initialiser/definition. GCC is going against spec by not requiring a definiton at first occurance. If you use a non-specialised form of the variable e.g. MyConst<3>
you will get a similar error from GCC:
<source>: In instantiation of 'constexpr const float MyConst<3>':
<source>:10:18: required from here
<source>:3:40: error: uninitialized 'const MyConst<3>' [-fpermissive]
3 | template<int N> inline constexpr float MyConst;
| ^~~~~~~
ASM generation compiler returned: 1
<source>: In instantiation of 'constexpr const float MyConst<3>':
<source>:10:18: required from here
<source>:3:40: error: uninitialized 'const MyConst<3>' [-fpermissive]
3 | template<int N> inline constexpr float MyConst;
|
This can be fixed by provided a initial definition for MyConst, e.g.
// Use a "sensible default"
template<int N> inline constexpr float MyConst(0.0f);
// Provide a more general definition
template<int N> inline constexpr float MyConst = N*1.1f;
For the relevant part of the standard, see dcl.constexpr paragraph 1.
The constexpr specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template. The consteval specifier shall be applied only to the declaration of a function or function template. A function or static data member declared with the constexpr or consteval specifier is implicitly an inline function or variable. If any declaration of a function or function template has a constexpr or consteval specifier, then all its declarations shall contain the same specifier.
Problem is that the constexpr
needs to be initialized.
C++14 standard, from section 7.5.1 paragraph 9,
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized.
Then:
#include <iostream>
template<int N> inline constexpr float MyConst = 0.0f;
template<> inline constexpr float MyConst<1> = 1.1f;
template<> inline constexpr float MyConst<2> = 2.2f;
int main ()
{
std::cout << MyConst<1> << '\n';
return 0;
}
works well both for g++ and clang.
This is DR 1712.
One the one hand, constexpr
variables must be initialized, on the other a template
isn't a "thing" until it's instantiated, and hence does not necessarily need an initial value until then.
Unfortunately the standard doesn't currently exempt constexpr
variable templates from the requirement of having an initial value. So technically the code is ill-formed.
The corresponding GCC issue is #68012, questioning whether or not a diagnostic is required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With