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: ‘ini开发者_运维知识库t’ 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;
}
"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}); }
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_assert
s 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?
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;
}
Use following syntax:
LIVE DEMO
#include <initializer_list>
template<class T, int Length>
class Point
{
std::initializer_list<T> data;
public:
constexpr Point(std::initializer_list<T> init)
: data
(
init.size() == Length ?
init : throw 0
)
{}
};
int main()
{
constexpr Point<int, 3> a{{1,2,3}};
constexpr Point<int, 2> b{{1,2,3}}; // compile time error
}
Refer following SO.
EDIT: Interesting that works on GCC 4.8.1, but does not work on Clang 3.4. Maybe this is related to constexpr
of .size()
(afaik, in C++14 it is constexpr
).
I haven't really figured out what's going on here.
If I say
const std::initializer_list<double> li = { 1, 2.5, 3.7, 4.3 };
static_assert(li.size() == 4, "fail");
I get a complain that 'li' was not declared 'constexper'.
But if I say
constexpr std::size_t dSize(std::initializer_list<double> di)
{
return di.size();
}
then
static_assert(dSize({1, 2.5, 3.7, 4.3}) == 4, "failed"); // works
but
static_assert(dSize(li) == 4, "failed");
fails with "error: the value of 'li' is not usable in a constant expression"
This is all with -std=c++11
So, somehow, an initializer list passed into an arg list can be part of a constant expression, but an initializer list just declared as a const variable can't.
精彩评论