array of class element as a static constexpr membe

2019-05-11 19:52发布

问题:

I have a bootstrap problem with a constexpr static member of a class Bar which is an array of Bar itself. Consider the following perfectly correct code:

struct Foo {
  int i;
  static const std::array<Foo, 2> A;
};
const std::array<Foo, 2> Foo::A {{{1},{2}}};

Now I'd like to have Foo::A not only const but also constexpr. I'm faced with the problem that static constexpr member initialization must be done inside the class declaration. However, since the declaration is not yet finished, the compiler doesn't yet know the size of an instance and therefore refuse to make the array. For example

 struct Bar {
   int i;
   constexpr static const std::array<Bar, 2> A{{{1},{2}}};
 };

is refused with

/usr/include/c++/4.8/array: In instantiation of ‘struct std::array<Bar, 2ul>’:
ess.cpp:14:56:   required from here
/usr/include/c++/4.8/array:97:56: error: ‘std::array<_Tp, _Nm>::_M_elems’ has incomplete type
       typename _AT_Type::_Type                         _M_elems;

Is there a way to solve that ? Or a workaround ?

回答1:

This is not possible at the moment, the compiler cannot know in advance if the constexpr is actually allowed / possible. Replace the member A with a function and it should work:

struct Bar
{
    int i;
    constexpr static std::array<Bar, 2> get_A()
    {
        return {{{1}, {2}}};
    }
};

Related (almost duplicate): static constexpr member of same type as class being defined



回答2:

I've found the following solution which ensure that

  1. The array is computed at compile time
  2. The array is stored in the struct without being copied.

The idea is to use a const reference on a constexpr array.

struct Bar {
  int i;
  static const std::array<Bar, 2> &A;
};

constexpr const std::array<Bar, 2> BarA {{{1},{2}}};
const std::array<Bar, 2> &Bar::A = BarA;


回答3:

The solution is actually just to only include the constexpr keyword on the definition, but not the declaration:

struct Foo {
  int i;
  static const std::array<Foo, 2> A;
};
constexpr std::array<Foo, 2> Foo::A {{{1},{2}}};

This ensures that Foo::A is defined as an actual constant expression.

Live Demo

Credit goes to Richard Smith for this answer.