【发布时间】:2019-11-17 19:02:12
【问题描述】:
对于实现条件类型,我非常喜欢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 }会失败。呃。
我目前的解决方法是引入一个结构和 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 实际上是分支的产量时不起作用。有没有可读的简短解决方案?
【问题讨论】:
-
为什么是
N == std::size_t{8}而不是N == 8?是不是怕std::size_t不能持有8? -
@L.F.可能他要防止警告比较具有不同符号的类型。
-
@MarekR 嗯...我在
-Wall -Wextra -Werror -pedantic-errors下没有收到N == 8的警告。对于可能不会导致问题的文字,可能会自动抑制警告? -
我总是尽量保持较低的转换次数,我知道在恒定的编译时间上下文中没有必要 - 但这是我的习惯。
标签: c++ c++11 templates template-meta-programming conditional-types