确定模板参数包中"optimal"常见数值类型
Determining the "optimal" common numeric type in a template parameter pack
在模板参数包中确定常见数字类型的最佳方法是:
- 最小的尺寸
- 不损失精度,以及
- 将参数包中的任何类型转换为这种"理想"的通用类型时,是否没有上溢/下溢的风险
可变模板(best_common_numeric_type
)可以这样使用:
template<typename... NumericTypes>
auto some_numeric_func(const NumericTypes&...)
-> typename best_common_numeric_type<NumericTypes...>::type;
并具有如下实例化:
[1] best_common_numeric_type<long, unsigned long, float, double, int>::type = double
[2] best_common_numeric_type<unsigned int, unsigned long>::type = unsigned long
[3] best_common_numeric_type<signed int, signed long>::type = signed long
[4] best_common_numeric_type<signed int, unsigned int>::type = signed long
[5] best_common_numeric_type<signed int, unsigned long>::type = int128_t (maybe)
因此,例如,在情况[4]中,::type
必须是signed long
,因为signed int
不能在没有溢出风险的情况下保持unsigned int
,相反,unsigned int
不能在没有下溢风险的情况中保持signed int
。
这同样适用于[5],只是现在signed long
不再足够,因为它不能在没有溢出风险的情况下容纳unsigned long
。
(实现可能是特定于数据模型的,但你已经明白了。)
那么,在C++11中,实现这一目标的最佳方法是什么呢?
我参加聚会有点晚了,这是我没有Boost的解决方案:
#include <type_traits>
#include <cstdint>
template<class I, bool Signed> struct mk_signed { typedef I type; };
template<> struct mk_signed<uint8_t , true> { typedef int16_t type; };
template<> struct mk_signed<uint16_t, true> { typedef int32_t type; };
template<> struct mk_signed<uint32_t, true> { typedef int64_t type; };
template<> struct mk_signed<uint64_t, true> { typedef int64_t type; };
template <typename... Ts> struct best_common_numeric_type;
template <typename T> struct best_common_numeric_type<T> { typedef T type; };
template <typename T, typename... Ts>
struct best_common_numeric_type<T, Ts...> {
typedef typename best_common_numeric_type<Ts...>::type TS;
typedef typename std::conditional < (sizeof(T) > sizeof(TS)), T, TS>::type bigger_integral;
constexpr static bool fp = std::is_floating_point<T>::value || std::is_floating_point<TS>::value;
constexpr static bool have_signed = !fp && (std::is_signed<T>::value || std::is_signed<TS>::value);
typedef typename std::conditional <
fp,
typename std::common_type<T,TS>::type,
typename mk_signed<bigger_integral,have_signed>::type
>::type type;
};
您可以使用Boost Integer来选择合适的情况。
- http://www.boost.org/doc/libs/1_54_0/libs/integer/doc/html/boost_integer/integer.html#boost_integer.integer.sized
暂时忽略非积分元素类型的情况,下面是对所提出的情况的快速测试(GCC没有int128_t
):
在Coliru上直播
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/integer.hpp>
#include <limits>
using namespace boost;
namespace best_fit_
{
// wrappers around Boost Integer http://www.boost.org/doc/libs/1_54_0/libs/integer/doc/html/boost_integer/integer.html#boost_integer.integer.sized
template <bool is_signed, int bin_digits> struct select_int;
template <int bin_digits> struct select_int<true, bin_digits> {
using type = typename boost::int_t<bin_digits + 1>::least;
};
template <int bin_digits> struct select_int<false, bin_digits> {
using type = typename boost::uint_t<bin_digits>::least;
};
// query helper
struct digits {
template <typename I> using apply = mpl::int_<std::numeric_limits<I>::digits>;
};
}
template <typename... I>
struct best_common_integral
{
private:
using Ints = mpl::vector<I...>;
using Bits = typename mpl::transform<Ints, best_fit_::digits>::type;
template <typename J>
struct is_signed { static constexpr bool value = std::numeric_limits<J>::is_signed; };
using max = typename mpl::deref<typename mpl::max_element<Bits>::type>::type;
// sigh, there is no `mpl::any`, AFAICT
using sign = typename mpl::fold<
Ints,
mpl::bool_<false>,
mpl::if_<is_signed<mpl::_2>, mpl::bool_<true>, mpl::_1>
>::type;
public:
using type = typename best_fit_::select_int<sign::value, max::value>::type;
};
#include <typeinfo>
#include <iostream>
#include <cassert>
int main()
{
using case1 = best_common_integral<long, unsigned long, float, double, int>;
using case2 = best_common_integral<unsigned int, unsigned long>;
using case3 = best_common_integral<signed int, signed long>;
using case4 = best_common_integral<signed int, unsigned int>;
using case5 = best_common_integral<signed int, unsigned long>;
//assert(typeid(case1::type) == typeid(double));
assert(typeid(case2::type) == typeid(unsigned long));
assert(typeid(case3::type) == typeid(signed long));
assert(typeid(case4::type) == typeid(signed long));
//assert(typeid(case5::type) == typeid(int128_t (maybe)));
}
注意:不知怎么的,我脑子里一直想着你需要C++03来完成这项工作。这对于C++11来说可以简化。这也不会选择最小的尺寸
据我所知,这并没有什么标准,但它是可以做到的:http://coliru.stacked-crooked.com/view?id=c6aa42345f91ab51d745d56573b15a04-4f3a5fd633ef9f45cb08e23efe0a
首先;思想家;结构。
template<bool isfloat, bool negative> struct best_numeric_type
{typedef long double type;};
template<> struct best_numeric_type<false, true>
{typedef long long type;};
template<> struct best_numeric_type<false, false>
{typedef unsigned long long type;};
然后是基本情况:
template<class T> struct best_common_numeric_type1 {
static const bool isfloat=false;
static const bool negative=false;
typedef typename best_numeric_type<isfloat, negative>::type type;
};
template<> struct best_common_numeric_type1<char> {
static const bool isfloat=false;
static const bool negative=true;
typedef typename best_numeric_type<isfloat, negative>::type type;
};//copy-paste for signed char, short, int, long, and long long.
template<> struct best_common_numeric_type1<float> {
static const bool isfloat=true;
static const bool negative=false;
typedef typename best_numeric_type<isfloat, negative>::type type;
};//copy-paste for double and long double.
然后加入者:
template<class First, class Second>
struct best_common_numeric_type2 {
static const bool isfloat = best_common_numeric_type1<First>::isfloat | best_common_numeric_type1<Second>::isfloat;
static const bool negative = best_common_numeric_type1<First>::negative | best_common_numeric_type1<Second>::negative;
typedef typename best_numeric_type<isfloat, negative>::type type;
};
template<class First, class Second, class Third>
struct best_common_numeric_type3 {
static const bool isfloat = best_common_numeric_type2<First, Second>::isfloat | best_common_numeric_type1<Third>::isfloat;
static const bool negative = best_common_numeric_type2<First, Second>::negative | best_common_numeric_type1<Third>::negative;
typedef typename best_numeric_type<isfloat, negative>::type type;
};
template<class First, class Second, class Third, class Fourth>
struct best_common_numeric_type4 {
static const bool isfloat = best_common_numeric_type3<First, Second, Third>::isfloat | best_common_numeric_type1<Fourth>::isfloat;
static const bool negative = best_common_numeric_type3<First, Second, Third>::negative | best_common_numeric_type1<Fourth>::negative;
typedef typename best_numeric_type<isfloat, negative>::type type;
};
template<class First, class Second, class Third, class Fourth, class Fifth>
struct best_common_numeric_type5 {
static const bool isfloat = best_common_numeric_type4<First, Second, Third, Fourth>::isfloat | best_common_numeric_type1<Fifth>::isfloat;
static const bool negative = best_common_numeric_type4<First, Second, Third, Fourth>::negative | best_common_numeric_type1<Fifth>::negative;
typedef typename best_numeric_type<isfloat, negative>::type type;
};
最后是一个测试:
#include <typeinfo>
#include <iostream>
void printer(long double) {std::cout << "long doublen";}
void printer(unsigned long long) {std::cout << "ulln";}
void printer(long long) {std::cout << "lln";}
void printer(...) {std::cout << "elsen";}
int main() {
printer(best_common_numeric_type5<long, unsigned long, float, double, int>::type());
printer(best_common_numeric_type2<unsigned int, unsigned long>::type());
printer(best_common_numeric_type2<signed int, signed long>::type());
printer(best_common_numeric_type2<signed int, unsigned int>::type());
printer(best_common_numeric_type2<signed int, unsigned long>::type());
printer(best_common_numeric_type2<float, char>::type());
}
结果:
long double
ull
ll
ll
ll
long double
相关文章:
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组的地址分配给变量并删除
- 从C++本机插件更新Vector3数组
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 数组索引的值没有增加
- 将对象数组的引用传递给函数
- 为char数组调整zlib-zpipe
- 2D数组来自文本输入,中间有空格
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- 使用strcpy将char数组的元素复制到另一个数组
- 如何从向量或数组中选择最常见的数字?(前五名)C++
- 模板对象的C 数组:施放到常见的非类型模板参数是UB吗?
- 从列表到数组C 查找模式或最常见的数字
- 数组中最常见的数字范围
- C++ - 查找倍数并消除常见数
- 确定模板参数包中"optimal"常见数值类型
- 填充字节发现以及数组初始值设定项中多余元素的常见和特殊编译器行为
- 在C++中查找数组的模式(最常见的元素)