Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_assert on initializer_list::size()

Why is std::initializer_list<_E>::size not allowable in a static_assert, even though it's declared as a constexpr in my libstdc++ (v. 4.6)?

For example, the following code:

template<class T, int Length>
class Point
{
  public:
    Point(std::initializer_list<T> init)
    {
      static_assert(init.size() == Length, "Wrong number of dimensions");
    }
};

int main()
{
  Point<int, 3> q({1,2,3});

  return 0;
}

gives the following error:

test.C: In constructor ‘Point<T, Length>::Point(std::initializer_list<_Tp>) [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<_E>::size [with _E = int, std::initializer_list<_E>::size_type = long unsigned int]()’
test.C:54:7: error: ‘init’ is not a constant expression

Note that this works just fine for a trivial example:

class A
{
  public:
    constexpr int size() { return 5; }
};

int main()
{
  A a;
  static_assert(a.size() == 4, "oh no!");

  return 0;
}
like image 837
rcv Avatar asked Mar 25 '11 22:03

rcv


3 Answers

"Initializer lists" are just horrible kludges.

Don't:

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

Do:

template<typename T, std::size_t N> void Do(const T(&list)[N]) { // Good!     static_assert(N == 3, "Exactly three elements are required."); }  void Test() { Do({1,2,3}); } 
like image 165
alecov Avatar answered Sep 22 '22 19:09

alecov


The compiler says that init is the problem, not init.size().

I guess that the constructor could be called from different places with different length initializers.

(To elaborate: You're trying to write a static_assert that depends on the run-time value of the variable init, namely how many elements it has. static_asserts have to be evaluable at the time the function is compiled. Your code is analogous to this trivially invalid example:)

void foo(int i) { static_assert(i == 42, ""); } int main() { foo(42); }  // but what if there's a caller in another translation unit? 
like image 29
Bo Persson Avatar answered Sep 26 '22 19:09

Bo Persson


From my discussion with @Evgeny, I realized that this just works (with gcc 4.8 c++11) and may as well do the size check by only accepting a compatible size in the initializer list (in main).

(code link: http://coliru.stacked-crooked.com/a/746e0ae99c518cd6)

#include<array>
template<class T, int Length>
class Point
{
  public:
    Point(std::array<T, Length> init)
    {
//not needed//      static_assert(init.size() == Length, "Wrong number of dimensions");
    }
};

int main()
{
  Point<int, 3> q({1,2,3}); //ok
//  Point<int, 3> q2({1,2,3,4}); //compile error (good!)
  Point<int, 3> q2({1,2}); // ok, compiles, same as {1,2,0}, feature or bug?
  return 0;
}
like image 36
alfC Avatar answered Sep 25 '22 19:09

alfC