Тернарный оператор не вычисляется во время компиляции — Метапрограммирование шаблонов

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?

Это не минимально воспроизводимый пример . Что такое Vector<>

user12002570 31.08.2024 04:47

Можно упростить с помощью template <int I, int... Xs> struct Get<T, Vector<Xs...>> { static constexpr int value = std::array<int, sizeof...(Xs)>{Xs...}[I]; };.

Jarod42 31.08.2024 10:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Даже если условие тернарного оператора известно во время компиляции, все операнды все равно должны быть действительными и соответствовать обычным правилам использования 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.

Другие вопросы по теме

Выбор унифицированного типа дистрибутива из стандартной библиотеки
Аргументы функции шаблона с переменным числом аргументов не принимаются
Специализация шаблона на C++ с использованием Enable_if
Специализация шаблона C++ STD внутри другого пространства имен
Почему определение оператора = (а не объявление) должно быть написано, когда подходящий шаблон легко доступен
Использование SFINAE в конструкторе, чтобы проверить, существует ли конструктор типа члена
Использование if-constexpr и концепций для обнаружения экземпляра определенного типа расширенной политики
Почему квалификатор const игнорируется при применении к выведенной ссылке Lvalue в C++?
Создание декартова произведения на основе аргумента шаблона целочисленного диапазона
Почему выведение std::call_once не удалось и возникла ошибка «не удалось вывести параметр шаблона ‘_Callable’»