查找最多可以计数到 N 的最小整数类型
Find the smallest integer type that can count up to N
我想要 C++03 中的解决方案,该解决方案允许我选择一种能够容纳最多 N
个整数同时保持尽可能小的类型。
基本上我只需要调用这样的元函数:
meta::tight_int<UpToN>::type n(0); // UpToN a size_t from a template. or static const
对于变量声明。使用 boost::mpl
是可以的,因为我理解它,但是我的项目中没有它,所以我将不得不将您的意图转换为我自己的元库。
如果对已签名/未签名有疑问,请考虑仅未签名。
一些不变量:
static_assert(meta::is_same<meta::tight_int<255>::type, uint8_t>::value, "")
static_assert(meta::is_same<meta::tight_int<256>::type, uint16_t>::value, "")
static_assert(meta::is_same<meta::tight_int<65536>::type, uint32_t>::value, "")
你明白了:)
你可以尝试这样的事情。 UpToN 类型在这里是一个模板参数,但您可以将其更改为 size_t。 我这样做是因为无符号的长长可能大于size_t。 为简单起见,此版本仅生成无符号类型。
此元程序遍历列表无符号整数类型,将upto_n转换为候选类型(Try_t(,然后返回到upto_n的类型(Max_t(,并检查其与原始upto_n是否相等。
如果强制转换保持这种相等性,并且Try_t的大小小于或等于Best_t的大小,则迭代将继续Try_t替换Best_t。
无符号字符专用化通过候选类型终止迭代。
#include <iostream>
template <typename T> struct next_t {};
template <> struct next_t<unsigned long long> { typedef unsigned long type; };
template <> struct next_t<unsigned long> { typedef unsigned int type; };
template <> struct next_t<unsigned int> { typedef unsigned short type; };
template <> struct next_t<unsigned short> { typedef unsigned char type; };
template <typename Max_t, Max_t upto_n, typename Best_t=Max_t, typename Try_t=unsigned long long, bool try_is_better = (sizeof(Try_t) <= sizeof(Best_t) && upto_n == Max_t(Try_t(upto_n)))>
struct tight_int {
typedef typename tight_int<Max_t, upto_n, Best_t, typename next_t<Try_t>::type>::type type;
};
template <typename Max_t, Max_t upto_n, typename Best_t, typename Try_t>
struct tight_int<Max_t, upto_n, Best_t, Try_t, true> {
typedef typename tight_int<Max_t, upto_n, Try_t, typename next_t<Try_t>::type>::type type;
};
template <typename Max_t, Max_t upto_n, typename Best_t>
struct tight_int<Max_t, upto_n, Best_t, unsigned char, true> {
typedef unsigned char type;
};
template <typename Max_t, Max_t upto_n, typename Best_t>
struct tight_int<Max_t, upto_n, Best_t, unsigned char, false> {
typedef Best_t type;
};
int main() {
typedef tight_int<size_t, 255>::type tight_255_t;
typedef tight_int<size_t, 256>::type tight_256_t;
typedef tight_int<size_t, 65535>::type tight_65535_t;
typedef tight_int<size_t, 65536>::type tight_65536_t;
std::cout << "255 : " << sizeof(tight_255_t) << std::endl;
std::cout << "256 : " << sizeof(tight_256_t) << std::endl;
std::cout << "65535 : " << sizeof(tight_65535_t) << std::endl;
std::cout << "65536 : " << sizeof(tight_65536_t) << std::endl;
}
此代码使用帮助程序类next_t来减少tight_int的专用计数。 但是tight_int仍然有 4 个专业(如果我们计算默认定义(。
我们可以通过引入一个辅助类将专用化计数减少一半,该辅助类可以根据 bool 参数 try_is_better 在Try_t和Best_t之间进行选择。 结果将传递到下一个迭代的Best_t。 此更改将给我们留下最少的专用化计数:默认定义(处理所有非专用类型(和迭代终止专用化处理无符号字符。 不幸的是,这种优化会影响可读性,并且往往会掩盖元程序的底层机制。
对于下面的版本,新的帮助程序类type_sel替换了tight_int的专用化,try_is_better真和假。 您可能会注意到模板参数列表语法真的开始失控了:
template <typename T> struct next_t {};
template <> struct next_t<unsigned long long> { typedef unsigned long type; };
template <> struct next_t<unsigned long> { typedef unsigned int type; };
template <> struct next_t<unsigned int> { typedef unsigned short type; };
template <> struct next_t<unsigned short> { typedef unsigned char type; };
// helper class type_sel which selects one of two types based on a static bool
template <bool, typename True_t, typename False_t>
struct type_sel { typedef True_t type; };
template <typename True_t, typename False_t>
struct type_sel<false, True_t, False_t> { typedef False_t type; };
// default definition of tight_int, handling all Try_t except unsigned char
template <typename Max_t, Max_t upto_n, typename Best_t = Max_t,
typename Try_t = unsigned long long,
bool try_is_better=(sizeof(Try_t)<=sizeof(Best_t) && upto_n==Max_t(Try_t(upto_n)))>
struct tight_int {
typedef typename tight_int<Max_t, upto_n,
typename type_sel<try_is_better, Try_t, Best_t>::type,
typename next_t<Try_t>::type>::type type;
};
// unsigned char specialization of tight_int terminates iteration through types
template <typename Max_t, Max_t upto_n, typename Best_t, bool try_is_better>
struct tight_int<Max_t, upto_n, Best_t, unsigned char, try_is_better> {
typedef typename type_sel<try_is_better, unsigned char, Best_t>::type type;
};
我仍然不喜欢的一件事是类型列表的蹩脚实现方式(如next_t(。 我不喜欢每个类型需要指定两次的方式:作为专用模板参数和 next_type::type。 相反,我们可以使用一个嵌套自身的类来形成类型列表。下面,Try_t替换为Trylist_t。 新的帮助程序类 tpair 将自身嵌套在模板类型参数中,以形成迭代的类型列表。 现在可以使用一行定义类型列表,如下所示:
tpair<unsigned long long, tpair<unsigned long, tpair<unsigned int, ... > >
并且此类型列表类可以在其他地方用于构建其他类型的列表。(请记住,我们的规范受 C++03 约束,因此可变参数模板参数列表不可用。
所以这是下一个版本,带有 tpair 类型列表。 我没有为模板参数列表中的换行符而烦恼,因为无论如何现在它都是不可读的:
template <typename My_t, typename Next_t=void>
struct tpair { typedef My_t type; typedef Next_t next_tpair; } ;
template <bool, typename True_t, typename False_t>
struct type_sel { typedef True_t type; };
template <typename True_t, typename False_t>
struct type_sel<false, True_t, False_t> { typedef False_t type; };
template <typename Max_t, Max_t upto_n, typename Best_t = Max_t, typename Trylist_t = tpair<unsigned long long, tpair<unsigned long, tpair<unsigned int, tpair<unsigned short, tpair<unsigned char> > > > >, bool try_is_better=(sizeof(Trylist_t::type)<=sizeof(Best_t) && upto_n==Max_t((typename Trylist_t::type) upto_n))>
struct tight_int {
typedef typename tight_int<Max_t, upto_n, typename type_sel<try_is_better, typename Trylist_t::type, Best_t>::type, typename Trylist_t::next_tpair>::type type;
};
template <typename Max_t, Max_t upto_n, typename Best_t, bool try_is_better>
struct tight_int<Max_t, upto_n, Best_t, typename tpair<unsigned char>, try_is_better> {
typedef typename type_sel<try_is_better, unsigned char, Best_t>::type type;
};
好吧,这不是一个完整的答案,我仍然发布它是为了:
- 帮助未来的 Google 员工了解 Boost 如何适用于此功能。
- 也许给他们一个替代克里斯托弗·奥克尔斯的方式。
这是我从提升技术派生的代码:
namespace detail
{
template< int Category > struct UintLeastHelper {}; // default is empty
// specializatons: 1=u64, 2=u32, 3=u16, 4=u8,
// no specializations for 0 and 5: requests are errors
template<> struct UintLeastHelper<1> { typedef u64 Least; };
template<> struct UintLeastHelper<2> { typedef u32 Least; };
template<> struct UintLeastHelper<3> { typedef u16 Least; };
template<> struct UintLeastHelper<4> { typedef u8 Least; };
}
//! finds the type that is the smallest that can bear numbers up-to-and-containing MaxValue.
template< u64 MaxValue >
struct TightestUInt_T
{
typedef typename detail::UintLeastHelper
<
1 + // (MaxValue <= IntegerTraits<u64>::ConstMax_T::value) <- this is always true of course.
(MaxValue <= IntegerTraits<u32>::ConstMax_T::value) +
(MaxValue <= IntegerTraits<u16>::ConstMax_T::value) +
(MaxValue <= IntegerTraits<u8>::ConstMax_T::value)
>::Least Value_T;
};
这通过了问题的static_assert
测试OK。
如您所见,这很有趣,因为它使用从转换为 int(0 或 1(的比较结果中的一系列添加来确定类别。
您还会看到它取决于一些较低级别的实用程序,ConstMax_T
。这是在常量表达式中工作的numeric_limits
的替代品。Boost有自己的系统,我也复制了。
基本上它的结局是这样的:
template <class T>
class IntegerTraits
{
public:
typename typedef TIsIntegral<T>::ValueType_t IsIntegral_T;
};
namespace detail
{
template <class T, T MinVal, T MaxVal>
class IntegerTraitsBase
{
public:
typedef TIntegralConstant<bool, true>::ValueType_t IsIntegral_T;
typedef TIntegralConstant<T, MinVal> ConstMin_T;
typedef TIntegralConstant<T, MaxVal> ConstMax_T;
};
}
template<>
class IntegerTraits<char>
: public detail::IntegerTraitsBase<char, CHAR_MIN, CHAR_MAX>
{ };
template<>
class IntegerTraits<signed char>
: public detail::IntegerTraitsBase<signed char, SCHAR_MIN, SCHAR_MAX>
{ };
// etc. for next types
你看到它最终再次使用一个甚至更低级别的实用程序TIntegralConstant
这真的很容易做到。所以这个答案比克里斯托弗的答案使用的代码要多得多,而且不能轻易地粘贴到ideone中。但我仍然发布它以展示我最终是如何做到的。原因是它通过解开基本函数来帮助扩展我自己的元库。
享受
- 整数类型应该显式转换(例如"int"到"无符号")还是只会增加混乱?
- 涉及旧式枚举和整数类型的重载解析
- 将字符串转换为整数类型T,检查是否存在溢出
- 整数文本太大,无法用任何整数类型表示--C++
- isdigit(c) - 字符或整数类型?
- 为什么对小于 4 个字节的整数类型的位操作会发生意外行为?
- C++模板专用化 - 将其他整数类型委托给uint64_t
- 何时应使用 C++ 固定宽度整数类型,它们如何影响性能?
- 根据浮点数选择最小整数类型
- 为什么 QVariant 将字符类型视为整数类型
- 我们可以在整数类型的双指针中分配2D整数数组的地址吗?怎么可能
- 默认情况下,决定整数类型是唱歌或无符号的类型
- 是否可以根据类型是整数类型还是浮点类型重载模板函数
- 使用固定整数类型的安全性
- 如何使用条件来检查类型名 T 是否是 C++ 中浮点类型的整数类型
- C++文本整数类型
- 哪种整数类型可以安全且便携式用于始终保持指针值
- 检测整数类型变量上的空白输入
- 为什么对Chrono :: Nanseconds的表示类型是签名的整数类型
- 如何处理警告:从较小的整数类型int转换为int*