Для реализации условного типа мне очень нравится std::conditional_t
, поскольку он делает код коротким и очень читабельным:
template<std::size_t N>
using bit_type =
std::conditional_t<N == std::size_t{ 8 }, std::uint8_t,
std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
std::conditional_t<N == std::size_t{ 32 }, std::uint32_t,
std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;
используя его, работает довольно интуитивно:
bit_type<8u> a; // == std::uint8_t
bit_type<16u> b; // == std::uint16_t
bit_type<32u> c; // == std::uint32_t
bit_type<64u> d; // == std::uint64_t
Но так как это чисто условный тип, должен быть тип по умолчанию - void
, в данном случае. Поэтому, если N
- любое другое значение, указанный тип дает:
bit_type<500u> f; // == void
Теперь это не компилируется, но тип выдачи все еще действителен.
Это означает, что вы могли бы сказать bit_type<500u>* f;
и иметь действительную программу!
Так есть ли хороший способ разрешить сбой компиляции при достижении случая сбоя условного типа?
Одной из идей немедленно было бы заменить последние std::conditional_t
на std::enable_if_t
:
template<std::size_t N>
using bit_type =
std::conditional_t<N == std::size_t{ 8 }, std::uint8_t,
std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
std::conditional_t<N == std::size_t{ 32 }, std::uint32_t,
std::enable_if_t< N == std::size_t{ 64 }, std::uint64_t>>>>;
Проблема в том, что шаблоны всегда полностью оцениваются, а это означает, что std::enable_if_t
всегда полностью оценивается - и это приведет к ошибке, если N != std::size_t{ 64 }
. Urgh.
Мой текущий обходной путь к этому довольно неуклюже, представляя структуру и 3 using
объявления:
template<std::size_t N>
struct bit_type {
private:
using vtype =
std::conditional_t<N == std::size_t{ 8 }, std::uint8_t,
std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
std::conditional_t<N == std::size_t{ 32 }, std::uint32_t,
std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;
public:
using type = std::enable_if_t<!std::is_same_v<vtype, void>, vtype>;
};
template<std::size_t N>
using bit_type_t = bit_type<N>::type;
static_assert(std::is_same_v<bit_type_t<64u>, std::uint64_t>, "");
Как правило, это работает, но мне не нравится, так как он добавляет так много вещей, что я мог бы просто использовать специализацию шаблонов. Он также резервирует void
в качестве особого типа - поэтому он не будет работать, когда void
на самом деле является доходностью от ветви. Есть ли читаемое, короткое решение?