template <int T, typename V>
struct Get;
template <int T>
struct Get<T, Vector<>> {
static_assert(std::false_type::value, "Vector Index out of bounds!");
};
template <int T, int X, int... Xs>
struct Get<T, Vector<X, Xs...>> {
static const int value = (T == 0) ? X : Get<T - 1, Vector<Xs...>>::value;
};
// ^ Your code goes here
static_assert(Get<0, Vector<0,1,2>>::value == 0);
I was trying this code to get a specific element of a vector. However, why do i get an error here as follows:
p1.cpp: In instantiation of ‘struct Get<-3, Vector<> >’:
p1.cpp:380:69: recursively required from ‘const int Get<-1, Vector<1, 2> >::value’
p1.cpp:380:69: required from ‘const int Get<0, Vector<0, 1, 2> >::value’
p1.cpp:385:38: required from here
p1.cpp:375:40: error: static assertion failed: Vector Index out of bounds!
375 | static_assert(std::false_type::value, "Vector Index out of bounds!");
| ~~~~~~~~~~~~~~~~~^~~~~
p1.cpp:375:40: note: ‘std::integral_constant<bool, false>::value’ evaluates to false
p1.cpp: In instantiation of ‘const int Get<-2, Vector<2> >::value’:
p1.cpp:380:69: recursively required from ‘const int Get<-1, Vector<1, 2> >::value’
p1.cpp:380:69: required from ‘const int Get<0, Vector<0, 1, 2> >::value’
p1.cpp:385:38: required from here
p1.cpp:380:76: error: ‘value’ is not a member of ‘Get<-3, Vector<> >’
380 | static const int value = (T == 0) ? X : Get<T - 1, Vector<Xs...>>::value;
Why is the ternary operator not evaluated properly, leading to T becoming -3?
Even if the condition of the ternary operator is known at compile-time, all operands still need to be valid and follow the normal odr-use rules. Get<T - 1, Vector<Xs...>>::value
must still be verified to be a valid expression, implying that Get<T - 1, Vector<Xs...>>
will be instantiated.
So you will keep on recursively instantiating Get<T - 1, Vector<Xs...>>
with T
and Xs...
becoming smaller and smaller, until you match your first partial specialization, which is then also instantiated and that instantiation fails for triggering the static_assert
.
To avoid the instantiation, you should declare a partial specialization instead of using the ternary operator:
template <int T, int X, int... Xs>
struct Get<T, Vector<X, Xs...>> {
static const int value = Get<T - 1, Vector<Xs...>>::value;
};
template <int X, int... Xs>
struct Get<0, Vector<X, Xs...>> {
static const int value = X;
};
Also, generally it would be better to use constexpr
instead of const
for value
.
Also, until relatively recently, a static_assert
that unconditionally fails independently of the template parameters, was IFNDR (ill-formed, no diagnostic required), meaning that the compiler was allowed to diagnose it and fail to compile it, regardless of whether it is ever instantiated.
That was changed only with CWG 2518. So static_assert(std::false_type::value, "Vector Index out of bounds!");
would not be guaranteed to behave in the way you want.
For detailed explanation and workarounds, see e.g. P2593.