8

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.

user2052436
  • 4,321
  • 1
  • 25
  • 46

3 Answers3

3

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.

user213305
  • 402
  • 2
  • 10
  • I understand that the `constexpr` needs to be initialized. My expectation was that with templated `constexpr` it would work in SFINAE fashion, i.e. as long as I am only using `MyConst<1>` and `MyConst<2>`, and not, say `MyConst<3>`, it would be ok. – user2052436 Apr 29 '21 at 15:32
  • So was mine, but the `constexpr`-ness of the initial declaration prevents that. You can make the original non-`constexpr` and make the template specialisations `constexpr` and it will behave more intuitively. https://godbolt.org/z/1MbYKbGva – user213305 Apr 29 '21 at 15:38
  • Thanks. That's a good solution. Another solution I see is to have a `template struct` with `static inline constexpr MyConst` inside; `struct` is specialized for required values of `N`. This will cause compile-time failure for wrong N-s. This approach will give me what I initially intended to have, no caveats, right? – user2052436 Apr 29 '21 at 15:50
  • If you try that, GCC finally notices the lack of definition for a constexpr object and fails to compile any example. https://godbolt.org/z/M67d5fnqd – user213305 Apr 29 '21 at 16:01
  • 1
    I meant this https://godbolt.org/z/a9GdYzEsc – user2052436 Apr 29 '21 at 16:14
2

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.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • I understand "that the constexpr needs to be initialized". My expectation was that with templated `constexpr` it would work in SFINAE fashion, i.e. as long as I am only using `MyConst<1>` and `MyConst<2>`, and not, say `MyConst<3>`, it would be ok. – user2052436 Apr 29 '21 at 15:30
  • As user202436 notices: 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. And constexpr definition means initialisation. – Jean-Baptiste Yunès Apr 29 '21 at 15:34
2

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.

rustyx
  • 80,671
  • 25
  • 200
  • 267