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
进行操作。然后整个
标识就多余了。@MarekR
conditional\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>>;