static_assert в файле initializer_list :: size ()

Почему std :: initializer_list :: размер недопустим в static_assert, хотя он объявлен как constexpr в моем libstdc ++ (v. 4.6)?

Например, следующий код:

template class Point { public: Point(std::initializer_list init) { static_assert(init.size() == Length, "Wrong number of dimensions"); } }; int main() { Point q({1,2,3}); return 0; } 

дает следующую ошибку:

 test.C: In constructor 'Point::Point(std::initializer_list) [with T = int, int Length = 3]': test.C:60:26: instantiated from here test.C:54:7: error: non-constant condition for static assertion test.C:54:73: in constexpr expansion of 'init.std::initializer_list::size [with _E = int, std::initializer_list::size_type = long unsigned int]()' test.C:54:7: error: 'init' is not a constant expression 

Обратите внимание, что это просто отлично для тривиального примера:

 class A { public: constexpr int size() { return 5; } }; int main() { A a; static_assert(a.size() == 4, "oh no!"); return 0; } 

Компилятор говорит, что проблема init, а не init.size ().

Я предполагаю, что конструктор можно было бы вызывать из разных мест с инициализаторами разной длины.

(Чтобы уточнить: вы пытаетесь написать static_assert который зависит от значения времени выполнения переменной init , а именно от количества элементов, которые он имеет. static_assert s должен быть static_assert во время компиляции функции. к этому тривиально недействительному примеру 🙂

 void foo(int i) { static_assert(i == 42, ""); } int main() { foo(42); } // but what if there's a caller in another translation unit? 

«Списки инициализаторов» – это просто ужасные kludges.

Не рекомендуется:

 #include  template void Dont(std::initializer_list list) { // Bad! static_assert(list.size() == 3, "Exactly three elements are required."); } void Test() { Dont({1,2,3}); } 

Делать:

 template void Do(const T(&list)[N]) { // Good! static_assert(N == 3, "Exactly three elements are required."); } void Test() { Do({1,2,3}); } 

Используйте следующий синтаксис:

LIVE DEMO

 #include  template class Point { std::initializer_list data; public: constexpr Point(std::initializer_list init) : data ( init.size() == Length ? init : throw 0 ) {} }; int main() { constexpr Point a{{1,2,3}}; constexpr Point b{{1,2,3}}; // compile time error } 

Обратитесь к SO .


EDIT: Интересно, что работает на GCC 4.8.1, но не работает на Clang 3.4. Возможно, это связано с constexpr .size() (afaik, в C ++ 14 это constexpr ).

Из моего обсуждения с @Evgeny я понял, что это просто работает (с gcc 4.8 c++11 ) и может также выполнить проверку размера, приняв только совместимый размер в списке инициализаторов (в main ).

(код ссылки: http://coliru.stacked-crooked.com/a/746e0ae99c518cd6 )

 #include template class Point { public: Point(std::array init) { //not needed// static_assert(init.size() == Length, "Wrong number of dimensions"); } }; int main() { Point q({1,2,3}); //ok // Point q2({1,2,3,4}); //compile error (good!) Point q2({1,2}); // ok, compiles, same as {1,2,0}, feature? return 0; }