【问题标题】:Mapping an integral template parameter value onto a primitive type将整数模板参数值映射到原始类型
【发布时间】:2018-11-26 19:59:44
【问题描述】:

我想将一个数字映射到一个类型。对于这个例子,我将创建一个函数,将 sizeof() 结果映射到有符号的原始类型。

我想知道在现代 C++ 中是否有更好的方法来执行我在下面所做的事情,即获取模板值并将其转换为类型。现在这适用于将大小转换为已知类型,但我似乎无法在标准库中找到任何我想要的东西。我错过了什么吗?

如果没有,是否有更好的方法来执行此操作或清理此代码?例如,如果将来我们以某种方式最终拥有 128 位类型,那么这将不支持。

#include <iostream>
#include <type_traits>

template <size_t S>
static constexpr auto sizeToType() {
    static_assert(S == 1 or S == 2 or S == 4 or S == 8, "Bad type size");

    if constexpr (S == 1)
        return int8_t{};
    else if constexpr (S == 2)
        return int16_t{};
    else if constexpr (S == 4)
        return int32_t{}; 
    else
        return int64_t{};
}

int main() {
    using MY_TYPE = decltype(sizeToType<2>());

    MY_TYPE myType = MY_TYPE(0xFFFFFFFFFFFFFFFEUL);

    std::cout << sizeof(MY_TYPE) << " bytes" << std::endl;
    std::cout << "MY_TYPE(0xFFFFFFFFFFFFFFFEUL) = " << myType << std::endl;
}

输出(如预期):

2 bytes
MY_TYPE(0xFFFFFFFFFFFFFFFEUL) = -2

【问题讨论】:

  • 您的解决方案有什么问题?鉴于您编写的代码只能使用现代 c++,我想知道问题是什么
  • @user463035818 正如我在帖子中所说,我想知道是否有更好的方法来做到这一点,或者如果引入新的方法可以扩展到任何原始大小类型(例如:@ 987654325@ 如看到 here 如果支持)自动而无需更新代码。我也想知道是否有一种不那么冗长和不那么脆弱的方式来做我所做的事情。
  • 即使您在示例中使用的类型也只是可选的,并且是provided only if the implementation directly supports the type
  • 我们是否同意您的解决方案不支持任何类型,因为 size 确实是获得类型平等的糟糕方法?也就是说,您可以使用支持类型的元组并在其中找到与大小匹配的类型

标签: c++ templates c++17


【解决方案1】:

我不会为此使用 C++17 if constexpr,而是使用模板专业化,因为它看起来更具声明性。

大致如下:

template<size_t S> struct SizeToType {static_assert(S != S, "Wrong size"); };

template<> struct SizeToType<1> { using type = uint8_t; };
template<> struct SizeToType<2> { using type = uint16_t; };
template<> struct SizeToType<4> { using type = uint32_t; };
template<> struct SizeToType<8> { using type = uint64_t; };

template<size_t S>
using SizeToToTypeT = typename SizeToType<S>::type;

添加更多类型只是在此处添加更多专业化(单行)。

【讨论】:

  • 主节点太接近格式错误的 NDR。最好不要定义它。
  • @StoryTeller 我想要有意义的诊断。但我不确定,为什么会是 NDR? (除了我现在正在纠正的S == S 中的类型)。
  • 喜欢这些案例的 90%。它是 [temp.res]/8。这种情况并不完全依赖。所以一个聪明的实现可能会直接诊断出主要的。这是一个黑暗的角落。
  • @StoryTeller 它有很多案例。你能指出违反的那个吗?
  • 我在移动设备上,所以我不能轻易做到(cxxdraft-gen 很难导航)。否则我早就这样做了。我喜欢表格方法。
【解决方案2】:

不是一个很好的解决方案...但只是为了好玩...

我提出了一个类型特征(带有帮助器并用于选择内部类型),给定一个数字(字节数)和一个类型列表,选择列表中sizeof()大于或等于的第一个类型(如果没有这种类型,则什么都没有)

template <std::size_t, typename, typename = std::true_type>
struct sizeTypeH;

template <std::size_t Dim>
struct sizeTypeH<Dim, std::tuple<>, std::true_type>
 { };  // no type found

template <std::size_t Dim, typename T0, typename ... Ts>
struct sizeTypeH<Dim, std::tuple<T0, Ts...>,
                 std::integral_constant<bool, (sizeof(T0) >= Dim)>>
 { using type = T0; };

template <std::size_t Dim, typename T0, typename ... Ts>
struct sizeTypeH<Dim, std::tuple<T0, Ts...>,
                 std::integral_constant<bool, (sizeof(T0) < Dim)>>
   : public sizeTypeH<Dim, std::tuple<Ts...>>
 { };

template <std::size_t Dim, typename ... Ts>
struct sizeType : public sizeTypeH<Dim, std::tuple<Ts...>>
 { };

template <std::size_t Dim, typename ... Ts>
using sizeType_t = typename sizeType<Dim, Ts...>::type;

现在另一个using 接收一个整数,使用intX_t 类型的有序列表调用此类型特征

template <std::size_t Dim>
using intType_t = sizeType_t<Dim, std::int8_t, std::int16_t, std::int32_t,
                             std::int64_t>;

在我的平台上,我验证了以下static_assert()

   using t8  = intType_t<1u>;
   using t16 = intType_t<2u>;
   using t32 = intType_t<4u>;
   using t64 = intType_t<8u>;

   static_assert( std::is_same<t8, std::int8_t>{}, "!" );
   static_assert( std::is_same<t16, std::int16_t>{}, "!" );
   static_assert( std::is_same<t32, std::int32_t>{}, "!" );
   static_assert( std::is_same<t64, std::int64_t>{}, "!" );

如果明天引入int128_t,您可以简单地将其添加到intType_t using 定义中。

请注意:不保证满足前面的static_assert()

首先因为标准保证一个字节至少为8位;但可以超过 8 位。如果一个字节是 16 位的,从

using t16 = intType_t<2u>;

你会得到一种 32 位。

其次,因为intX_t 类型是可选的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多