#c #if-statement #c 17 #typetraits #variable-templates
#c #if-statement #c 17 #typetraits #переменная-шаблоны
Вопрос:
Рассмотрим следующий код
#include <type_traits>
template<bool Test, class T, T val1, T val2>
constexpr T if_v = std::conditional_t<Test,
std::integral_constant<T, val1>,
std::integral_constant<T, val2>>::value;
int main()
{
constexpr size_t value1 = 123;
constexpr size_t value2 = 456;
constexpr bool test = (3 > 2);
constexpr size_t r0 = if_v<test, size_t, value1, value2>; // = 123
return 0;
}
Поскольку во время компиляции мы знаем, что такое типы value1
и value2
, нам не нужно указывать это. Итак, мы могли бы написать
template<bool Test, auto val1, auto val2>
constexpr decltype(val1) if_v = std::conditional_t<Test,
std::integral_constant<decltype(val1), val1>,
std::integral_constant<decltype(val2), val2>>::value;
чтобы мы могли написать упрощенный оператор if if_v<test, value1, value2>
(без типа). В идеале я также хотел бы убедиться, что оба входных значения имеют один и тот же тип. Но я не уверен, как добиться этого при использовании auto
.
В принципе, есть ли лучшие способы определить if_v
так, чтобы мы могли писать if_v<test, value1, value2>
без необходимости указывать тип, а также каким static_assert
-то образом изменять равенство типов?
Комментарии:
1. Одним из решений является создание резервного класса для if_v, который выполняет статическое утверждение в своем теле. Я попробовал это, и это сработало. Я воздержусь от публикации этого в качестве ответа, поскольку подозреваю, что есть более простые или более элегантные решения (если это так, я полагаю, что кто-то еще их опубликует).
2. Разве вы не можете просто использовать
? :
? Чего мне не хватает?
Ответ №1:
Я также хотел бы убедиться, что оба входных значения имеют один и тот же тип. Но я не уверен, как добиться этого при использовании auto.
Как насчет использования SFINAE?
Я имею в виду
template <bool Test, auto v1, auto v2,
std::enable_if_t<std::is_same_v<decltype(v1), decltype(v2)>, int> = 0>
constexpr auto if_v = std::conditional_t<Test,
std::integral_constant<decltype(v1), v1>,
std::integral_constant<decltype(v2), v2>>::value;
или, может быть, просто
template <bool Test, auto v1, auto v2,
std::enable_if_t<std::is_same_v<decltype(v1), decltype(v2)>, int> = 0>
constexpr auto if_v = Test ? v1 : v2;
Комментарии:
1. О, очень приятно 🙂 Ограничение шаблона здесь намного лучше, чем косвенное использование.
2. Второе простое решение — это то, что я искал, большое спасибо!
Ответ №2:
Как это часто бывает, вы можете решить эту проблему, добавив еще один уровень косвенности. Создайте свою первую версию if_v
, которая явно использует тип в деталях реализации:
template<bool Test, class T, T val1, T val2>
constexpr T if_v_impl = std::conditional_t<Test,
std::integral_constant<T, val1>,
std::integral_constant<T, val2>>::value;
Теперь вы можете реализовать версию с выведенными типами заполнителей, проверив, совпадают ли выведенные типы, и вызывая только if_v_impl
в этом случае:
template<bool Test, auto val1, auto val2>
constexpr decltype(val1) if_v = std::is_same_v<decltype(val1), decltype(val2)>
? if_v_impl<Test, decltype(val1), val1, val2>
: throw;
Для простоты я throw
использую false, поскольку это не постоянное выражение, и его достаточно для остановки компиляции. Вы, конечно, можете создать пользовательскую диагностику, если действительно хотите, например, путем делегирования другой функции, которая static_assert
находится внутри тела.
Вот демонстрация.