Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Templated constexpr variable [duplicate]

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.

like image 430
user2052436 Avatar asked Apr 29 '21 14:04

user2052436


People also ask

What does constexpr mean in c++?

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.

Does constexpr imply inline?

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.

Does constexpr imply static?

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.

Can constexpr throw?

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.


Video Answer


3 Answers

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.

like image 87
user213305 Avatar answered Oct 23 '22 15:10

user213305


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.

like image 30
Jean-Baptiste Yunès Avatar answered Oct 23 '22 14:10

Jean-Baptiste Yunès


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.

like image 28
rustyx Avatar answered Oct 23 '22 15:10

rustyx