Альтернативы многим вложенным std::conditional_t?
Я нахожу много вложенных std::conditional_t трудными для чтения, поэтому я выбираю другой шаблон (вызова decltype для функции с автоматическим типом возврата):
template<bool is_signed, std::size_t has_sizeof>
auto find_int_type(){
static_assert(sizeof(int)==4);
if constexpr(is_signed){
if constexpr(has_sizeof==4){
return int{};
} else if constexpr (has_sizeof==8){
return std::int64_t{};
} else {
return;
}
} else {
if constexpr(has_sizeof==4){
return (unsigned int){};
}
else if constexpr (has_sizeof==8){
return std::uint64_t{};
} else {
return;
}
}
}
static_assert(std::is_same_v<int, decltype(find_int_type<true, 4>())>);
static_assert(std::is_same_v<unsigned int, decltype(find_int_type<false, 4>())>);
static_assert(std::is_same_v<void, decltype(find_int_type<false, 3>())>);
static_assert(std::is_same_v<void, decltype(find_int_type<false, 5>())>);
static_assert(std::is_same_v<std::int64_t, decltype(find_int_type<true, 8>())>);
static_assert(std::is_same_v<std::uint64_t, decltype(find_int_type<false, 8>())>);
static_assert(std::is_same_v<void, decltype(find_int_type<false, 9>())>);
Мои вопросы:
Есть ли способ лучше?
Является ли этот способ медленнее для компиляции, чем std::conditional_t (если предположить, что типы, которые мне нужно создать, гораздо шире, чем в этом примере, где я просто использую встроенные типы).
PS Это игрушечный пример, IRCode Я бы имел дело с некоторыми более сложными типами.
2 ответа
Лично я чувствую, что самый ясный подход здесь - "управляемый данными". Помещение критериев в таблицу (написанную как специализация шаблона класса) и предоставление компилятору возможности сопоставления с образцом для определения типа короче, меньше подвержено ошибкам и легче читается или расширяется.
template<bool is_signed, std::size_t has_sizeof>
struct find_int_type_impl { using type = void; }; // Default case
template<> struct find_int_type_impl<true, 4> { using type = std::int32_t; };
template<> struct find_int_type_impl<true, 8> { using type = std::int64_t; };
template<> struct find_int_type_impl<false, 4> { using type = std::uint32_t; };
template<> struct find_int_type_impl<false, 8> { using type = std::uint64_t; };
template<bool is_signed, std::size_t has_sizeof>
using find_int_type = typename find_int_type_impl<is_signed, has_sizeof>::type;
Поскольку std::disjunction<Args...>
наследует от первого типа в Args...
чья value
является true
или, если такого типа не существует, последний тип в Args...
мы можем (ab) использовать его для создания многопутевой ветви:
template<class... Args>
using select = typename std::disjunction<Args...>::type;
template<bool V, class T>
struct when {
static constexpr bool value = V;
using type = T;
};
template<bool is_signed, std::size_t has_sizeof>
using find_int_type = select<
when<is_signed, select<
when<has_sizeof==4, int>,
when<has_sizeof==8, std::int64_t>,
when<false, void>
>>,
when<!is_signed, select<
when<has_sizeof==4, unsigned int>,
when<has_sizeof==8, std::uint64_t>,
when<false, void>
>>
>;