Невозможно построить массив constexpr из списка braced-init-list

Я реализовал массив constexpr следующим образом:

template <typename T>
class const_array {
  const T* p;
  unsigned n;
public:
  template <unsigned N>
  constexpr const_array(const T(&a)[N]): p(a), n(N) { }

  constexpr unsigned size() const { return n; }
};

int main(int argc, char* argv[]) {
  // works
  static_assert(const_array<double>{{1.,2.,3.}}.size() == 3);

  // doesn't compile
  constexpr const_array<double> a{{1.,2.,3.}};
  static_assert(a.size() == 3);
}

Почему первая компиляция static_assert, но инициализация a терпит неудачу? Я использую gcc 6.2.0. Я получаю

: In function 'int main(int, char**)':
: error: 'const_array<double>{((const double*)(&<anonymous>)), 3u}' is not a constant expression
   constexpr const_array<double> a{{1.,2.,3.}};
                                        ^
test/const_array.cc:17:3: error: non-constant condition for static assertion
   static_assert(a.size() == 3);
   ^~~~~~~~~~~~~

Ответы

Ответ 1

Компилятор жалуется, что инициализатор a.p не является постоянным выражением. Это не соответствует §5.20/5.2:

если значение имеет тип указателя, оно содержит адрес объекта со статической продолжительностью хранения, адрес после конца такого объекта (5.7), адрес функции или значение нулевого указателя

Другими словами, только значения указателя, известные компоновщику, являются действительными константами. (Кроме того, в вашем примере указатель свисает.)

Первый static_assert не отключает это, потому что p отбрасывается, а значение n является постоянным выражением. Константные выражения могут иметь непостоянные подвыражения.

Это работает:

static constexpr double arr[] = { 1.,2.,3. };
constexpr const_array<double> a{ arr };
static_assert( a.size() == 3 );

Кредит @Jarod42 за то, что он указал на проблему в комментариях.

Ответ 2

Проблема заключалась в том, что вы не можете наследовать постоянный характер указателя на T во внутреннем шаблоне через параметр внешнего шаблона T.

template <typename T> class const_array {
    const T * p;
    unsigned n;
public:
    template <unsigned N>
        constexpr const_array(const T(& a)[N]): p(a), n(N) { }
};

int main(int argc, char* argv[]) {
    constexpr const_array<double> ca{(const double []) { 1., 2. }};
    return 0;
}

Я попробовал несколько десятков перестановок, чтобы избавиться от приведения без успеха.