C++ 有没有一种很好的方法来实现带有默认失败案例的条件类型?
对于实现条件类型,我非常喜欢,因为它使代码保持简短且可读性强:C++ 有没有一种很好的方法来实现带有默认失败案例的条件类型?,c++,c++11,templates,template-meta-programming,conditional-types,C++,C++11,Templates,Template Meta Programming,Conditional Types,对于实现条件类型,我非常喜欢,因为它使代码保持简短且可读性强: 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
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\u type*f代码>并将有一个有效的程序
那么,当达到条件类型的失败案例时,有没有一种好方法让编译失败
一个想法是立即将最后一个std::conditional\u t
替换为:
这通常是有效的,但我不喜欢它,因为它添加了太多东西,我还不如使用模板专门化。它还将void
保留为一种特殊类型-因此,当void
实际上是分支机构的收益时,它将不起作用。有一个可读的、简短的解决方案吗?您可以通过添加一个间接级别来解决这个问题,这样最外层的条件\u t
的结果不是一个类型,而是一个需要应用于它的元函数::type
。然后使用enable\u if
而不是enable\u if\u t
,这样您就不会访问::键入,除非实际需要:
template<typename T> struct identity { using type = T; };
template<std::size_t N>
using bit_type = typename
std::conditional_t<N == std::size_t{ 8 }, identity<std::uint8_t>,
std::conditional_t<N == std::size_t{ 16 }, identity<std::uint16_t>,
std::conditional_t<N == std::size_t{ 32 }, identity<std::uint32_t>,
std::enable_if<N == std::size_t{ 64 }, std::uint64_t>>>>::type;
模板结构标识{using type=T;};
模板
使用bit_type=typename
std::conditional_t::type;
在这个版本中,最后一个分支中的类型是enable\u if
,这始终是一个有效的类型,并且只有在实际执行该分支并且需要enable\u if::type
时,才会出现错误。当使用一个早期的分支时,您最终会对一个较小的整数类型使用identity::type
,如果没有嵌套类型(因为您不使用它),则启用并不重要。只是为了好玩。。。使用std::tuple
和std::tuple\u元素
如何避免使用std::conditional
如果可以使用C++14(因此模板变量和模板变量的专门化),则可以在元组中为转换大小/索引编写模板变量
template <std::size_t>
constexpr std::size_t bt_index = 100u; // bad value
template <> constexpr std::size_t bt_index<8u> = 0u;
template <> constexpr std::size_t bt_index<16u> = 1u;
template <> constexpr std::size_t bt_index<32u> = 2u;
template <> constexpr std::size_t bt_index<64u> = 3u;
如果只能使用C++11,则可以开发一个返回正确(或不正确)值的bt_index()
constepr
函数
您可以验证您是否满意
static_assert( std::is_same_v<bit_type<8u>, std::uint8_t>, "!" );
static_assert( std::is_same_v<bit_type<16u>, std::uint16_t>, "!" );
static_assert( std::is_same_v<bit_type<32u>, std::uint32_t>, "!" );
static_assert( std::is_same_v<bit_type<64u>, std::uint64_t>, "!" );
导致编译错误
--编辑--正如Jonathan Wakely所建议的那样,如果您可以使用C++20,那么std::ispow2()
和std::log2p1()
,您可以简化很多:您可以完全避免bt_索引
,只需编写
template <std::size_t N>
using bit_type = std::tuple_element_t<std::ispow2(N) ? std::log2p1(N)-4u : -1,
std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;
模板
使用bit\u type=std::tuple\u元素\u t;
为什么N==std::size\u t{8}
而不是N==8
?您是否担心std::size\t
无法容纳8
?@L.F.可能他有什么办法来防止使用不同符号比较类型的警告。@MarekR-Hmm。。。在-Wall-Wextra-Werror-pedantic errors
下,我没有收到带有N==8的警告。对于可能不会引起问题的文本,可能会自动取消警告?我总是尝试保持较低的强制转换次数,我知道在恒定的编译时上下文中这是不必要的,但这是我的习惯。因此这是一种罕见的情况,\t
版本实际上有缺点。很有趣,谢谢@MarekR不,那样的话,条件的
对(不存在的)::type
进行操作。然后整个标识就多余了。@MarekRconditional\u t
获取要应用的类型::type
,否则您将编写typename conditional::type::type
。如果这听起来令人困惑,那就是@MarekR不要试图建议修复,请编译我的代码,并尝试编译您的“修复”版本。你会发现哪个符合OP的要求,哪个不符合。我的回答确实解释了为什么会这样做,以及为什么它会起作用。@MarekR但我不会混合intN\u t
和enable\u if
,而是混合identity
和enable\u if
。因为您需要添加一个间接级别。阅读问题。阅读答案。您的版本更简单,但不起作用:整洁的替代方案!我想,由于std::tuple
的allignment特性,它可能会增加大型案例的编译时间,这里不需要。在C++20中,您应该能够执行模板constepr std::size\u t bt\u index=std::ispow2(N)?标准:log2p1(N)-4:-1代码>(即,没有专门化变量模板)。@JonathanWakely-正是我想要的;谢谢给定std::ispow2()
和std::log2p1()
您可以完全避免bt_索引
,直接将表达式用作std::tuple_元素
的第一个参数。您确实可以:-)
template<typename T> struct identity { using type = T; };
template<std::size_t N>
using bit_type = typename
std::conditional_t<N == std::size_t{ 8 }, identity<std::uint8_t>,
std::conditional_t<N == std::size_t{ 16 }, identity<std::uint16_t>,
std::conditional_t<N == std::size_t{ 32 }, identity<std::uint32_t>,
std::enable_if<N == std::size_t{ 64 }, std::uint64_t>>>>::type;
template <std::size_t>
constexpr std::size_t bt_index = 100u; // bad value
template <> constexpr std::size_t bt_index<8u> = 0u;
template <> constexpr std::size_t bt_index<16u> = 1u;
template <> constexpr std::size_t bt_index<32u> = 2u;
template <> constexpr std::size_t bt_index<64u> = 3u;
template <std::size_t N>
using bit_type = std::tuple_element_t<bt_index<N>,
std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;
static_assert( std::is_same_v<bit_type<8u>, std::uint8_t>, "!" );
static_assert( std::is_same_v<bit_type<16u>, std::uint16_t>, "!" );
static_assert( std::is_same_v<bit_type<32u>, std::uint32_t>, "!" );
static_assert( std::is_same_v<bit_type<64u>, std::uint64_t>, "!" );
bit_type<42u> * pbt42;
template <std::size_t N>
using bit_type = std::tuple_element_t<std::ispow2(N) ? std::log2p1(N)-4u : -1,
std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;