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);
Я пытался использовать этот код, чтобы получить определенный элемент вектора. Однако почему я получаю следующую ошибку:
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;
Почему тернарный оператор не вычисляется должным образом, что приводит к тому, что T становится -3?
Можно упростить с помощью template <int I, int... Xs> struct Get<T, Vector<Xs...>> { static constexpr int value = std::array<int, sizeof...(Xs)>{Xs...}[I]; };
.
Даже если условие тернарного оператора известно во время компиляции, все операнды все равно должны быть действительными и соответствовать обычным правилам использования odr. Get<T - 1, Vector<Xs...>>::value
по-прежнему должно быть проверено как допустимое выражение, подразумевая, что Get<T - 1, Vector<Xs...>>
будет создан.
Таким образом, вы будете продолжать рекурсивно создавать экземпляр Get<T - 1, Vector<Xs...>>
, при этом T
и Xs...
будут становиться все меньше и меньше, пока не достигнете соответствия своей первой частичной специализации, которая затем также создается, и эта реализация завершается неудачно из-за запуска static_assert
.
Чтобы избежать создания экземпляра, вам следует объявить частичную специализацию вместо использования тернарного оператора:
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;
};
Кроме того, обычно вместо constexpr
лучше использовать const
вместо value
.
Кроме того, до сравнительно недавнего времени static_assert
, который безоговорочно терпит неудачу независимо от параметров шаблона, назывался IFNDR (неправильно сформированный, не требуется диагностика), что означало, что компилятору было разрешено диагностировать его и не скомпилировать, независимо от того, был ли он когда-либо создан экземпляр.
Это было изменено только в CWG 2518. Так что static_assert(std::false_type::value, "Vector Index out of bounds!");
не будет гарантированно вести себя так, как вы хотите.
Подробное объяснение и обходные пути см., например. Р2593.
Это не минимально воспроизводимый пример . Что такое
Vector<>