2

I'm trying to initialize a constexpr array using C++17 via the following:

template <size_t N>
struct CStr
{
    static constexpr std::array<int, N> getArr()
    {
        std::array<int, N> a;
        for (auto idx = 0; idx < a.size(); ++idx)
        {
            a[idx] = idx * idx;
        }
        return a;
    }
    static constexpr auto arr { getArr()};
};


int main()
{
    for (const auto &el : CStr<10>::arr)
    {
        std::cout << el << std::endl;
    }
}

However this results in the following compile error about getArr not being a constexpr function. Can someone help explain why ?

<source>: In instantiation of 'constexpr const std::array<int, 10> CStr<10>::arr':
<source>:18:27:   required from 'struct CStr<10>'
<source>:24:35:   required from here
<source>:18:39: error: 'static constexpr std::array<int, N> CStr<N>::getArr() [with long unsigned int N = 10]' called in a constant expression
   18 |     static constexpr auto arr { getArr()};
      |                                 ~~~~~~^~
<source>:9:41: note: 'static constexpr std::array<int, N> CStr<N>::getArr() [with long unsigned int N = 10]' is not usable as a 'constexpr' function because:
    9 |     static constexpr std::array<int, N> getArr()
      |                                         ^~~~~~
<source>:12:9: error: 'goto' is not a constant expression
   12 |         for (auto idx = 0; idx < a.size(); ++idx)
      |         ^~~
Compiler returned: 1   |                                         ^~~~~
user3882729
  • 1,339
  • 8
  • 11
  • 1
    You have cut off the error message, it still continues - and contains a hilarious remark about `goto` :D – dyp Dec 31 '20 at 11:17
  • 1
    The issue disappears when you initialize `std::array a{};`. clang has a much more intelligible error message for this case :) Also, using `-std=c++20` makes the error disappear. – dyp Dec 31 '20 at 11:18
  • This is probably https://wg21.link/p1331 – dyp Dec 31 '20 at 11:27
  • Thanks dyp! Would you mind letting us know the procedure used to locate the relevant working group paper? – user3882729 Dec 31 '20 at 11:35
  • If I had one, I'd tell you. I googled `constexpr initialization site:open-std.org` and it was the first hit :D – dyp Dec 31 '20 at 11:36
  • W.r.t. -std=c++2a making the error disappear (w/o explicit initialization), from your answer below does it imply then that it's a bug with both clang and gcc allowing it? For example even if I stop the setting loop iteration to a.size()-1, in the process reading uninitialized memory, it seems to build fine w/ "0" being read. – user3882729 Dec 31 '20 at 11:42
  • For example in the InlinedVector section at the end of the paper they seem to be OK w/ reading uninitialized memory in a ```constexpr``` context – user3882729 Dec 31 '20 at 11:46
  • Can you give me a full example regarding both what you're doing with `a.size()-1` and the `InlinedVector`? I don't quite get what you're referring to in either case :( – dyp Dec 31 '20 at 13:31
  • 1
    I was a little confused about https://godbolt.org/z/GM1cEG compiling OK with gcc. However it turns out it does not compile with clang.. Likely a compiler bug w/ the former. – user3882729 Dec 31 '20 at 17:02
  • Huh, yes, very interesting! – dyp Dec 31 '20 at 17:42
  • This is really confusing, especially the mention of `goto`. Futhermore, the error was at a completely different place than shown by clang. I only found it by successively commenting out code until I found an uninitialized integer 30 lines below the line mentioned in the error (but still in the same constexpr function). – mxmlnkn Jun 23 '22 at 16:59

1 Answers1

4

This is a restriction in C++ until C++20, see Permitting trivial default initialization in constexpr contexts: You have to initialize all variables inside of a constexpr function. The Standard gives the following example:

C++17

constexpr int uninit() {
  int a;                  // error: variable is uninitialized
  return a;
}

C++20

constexpr int uninit() {
  struct { int a; } s;
  return s.a;                   // error: uninitialized read of s.a
}

Note that the initialization itself is OK in C++20. Reading the indeterminate value (inside the "uninitialized" object) is not OK.


To fix your issue, initialize the array or switch to C++20:

static constexpr std::array<int, N> getArr()
{
    std::array<int, N> a{}; // now initialized!
    for (auto idx = 0; idx < a.size(); ++idx)
    {
        a[idx] = idx * idx;
    }
    return a;
}
dyp
  • 38,334
  • 13
  • 112
  • 177