为了实现条件类型,我非常喜欢 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

现在,它不会编译,但是yielding类型仍然有效。

意味着您可以说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 },它将失败。嗯

我目前对此的解决方法相当笨拙,它引入了一个struct和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实际上是分支的 yield 的情况下,它将不起作用。有没有可读的简短解决方案?

最佳答案

您可以通过添加间接级别来解决此问题,以便最外面的conditional_t的结果不是类型,而是需要对其应用::type的元函数。然后使用enable_if而不是enable_if_t,因此除非实际需要,否则您将不访问::type:

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;

在此版本中,最终分支中的类型为enable_if< condition , uint64_t>,它始终是有效类型,并且只有在实际采用该分支且需要enable_if<false, uint64_t>::type的情况下,您才会收到错误消息。当采用较早的分支之一时,您最终将identity<uintNN_t>::type用于较小的整数类型之一,并且enable_if<false, uint64_t>没有嵌套类型也没关系(因为您不使用它)。

07-25 23:01
查看更多