基于 C++ 中可能的最大输入范围推导 return 类型
derivation of return type based on max range of input possible in C++
我最近在一次 C++ 面试中被问到这个问题,我在那里
被要求改进下面的代码,当
将两个 int
的结果相加得到 long
和 return
需要相应地派生类型。
下面的代码失败了,因为基于 decltype()
的推导不够智能,无法根据输入值的实际范围进行识别,但类型和推导的 return 类型相同。因此,如果 T
是 int
,我们可能需要一些元编程模板技术来将 return 类型派生为 long
。
这怎么能概括任何提示或线索?
我觉得decltype()
对这里没有帮助。
#include<iostream>
#include<string>
#include<climits>
using namespace std;
template<typename T> auto adder(const T& i1, const T& i2) -> decltype(i1+i2)
{
return(i1+i2);
}
int main(int argc, char* argv[])
{
cout << adder(INT_MAX-10, INT_MAX-3) << endl; // wrong.
cout << adder<long>(INT_MAX-10, INT_MAX-3) << endl; // correct!!.
return(0);
}
Hence we need perhaps some metaprogramming template technique to derive the return type as long if T is int.
没那么简单。
如果 T
是 int
,您不确定 long
是否足够。
标准只这么说
1) int
(sizeof(int) * CHAR_BIT
) 的位数是 至少 16
2) long
(sizeof(long) * CHAR_BIT
) 的位数是 至少 32
3) sizeof(int) <= sizeof(long)
因此,如果编译器用 sizeof(int) == sizeof(long)
管理 int
,这是完全合法的并且
adder<long>(INT_MAX-10, INT_MAX-3);
不起作用,因为 long
可能不足以包含(没有溢出)两个 int
之间的总和。
我没有看到一个简单而优雅的解决方案。
我认为最好的是基于 C++11 引入了以下类型这一事实
1) std::int_least8_t
, 至少8位的最小整数类型
2) std::int_least16_t
, 至少16位的最小整数类型
3) std::int_least32_t
, 至少32位的最小整数类型
4) std::int_least64_t
, 至少64位的最小整数类型
C++11 还引入了 std::intmax_t
作为最大宽度整数类型。
所以我提出以下模板类型选择器
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
即,给定位数,定义相应的最小 "at least" 整数类型。
我还提出以下建议using
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
给定类型 T
,检测肯定包含两个 T
值之和的最小整数类型(位数至少为位数的整数T
加一)。
所以你的 adder()
就变成了
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
观察到 returned 值不只是
return i1 + i2;
否则你 return 类型正确但值错误:i1 + i2
计算为 T
值,因此你可能会溢出,然后将总和分配给 typeNext<T>
变量。
为避免此问题,您必须使用两个值 (typeNext<T>{i1}
) 之一初始化 typeNext<T>
临时变量,然后添加另一个 (typeNext<T>{i1} + i2
) 以获得 typeNext<T>
值,最后是 return 计算值。这样,总和计算为 typeNext<T>
总和,并且您没有溢出。
下面是一个完整的编译示例
#include <cstdint>
#include <climits>
#include <iostream>
#include <type_traits>
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
int main()
{
auto x = adder(INT_MAX-10, INT_MAX-3);
std::cout << "int: " << sizeof(int)*CHAR_BIT << std::endl;
std::cout << "long: " << sizeof(long)*CHAR_BIT << std::endl;
std::cout << "x: " << sizeof(x)*CHAR_BIT << std::endl;
std::cout << std::is_same<long, decltype(x)>::value << std::endl;
}
在我的 Linux 64 位平台中,int
是 32 位,long
是 64 位,x
也是 long
和 decltype(x)
是同一类型。
但这对我的平台来说是正确的;没有什么可以保证 long
和 decltype(x)
永远相同。
还要注意尝试获取两个 std::intmax_t
的总和的类型
std::intmax_t y {};
auto z = adder(y, y);
给出错误并且无法编译,因为没有为比 sizeof(std::intmax_t)*CHAR_BIT
更大的 N
定义 typeFor
。
我最近在一次 C++ 面试中被问到这个问题,我在那里
被要求改进下面的代码,当
将两个 int
的结果相加得到 long
和 return
需要相应地派生类型。
下面的代码失败了,因为基于 decltype()
的推导不够智能,无法根据输入值的实际范围进行识别,但类型和推导的 return 类型相同。因此,如果 T
是 int
,我们可能需要一些元编程模板技术来将 return 类型派生为 long
。
这怎么能概括任何提示或线索?
我觉得decltype()
对这里没有帮助。
#include<iostream>
#include<string>
#include<climits>
using namespace std;
template<typename T> auto adder(const T& i1, const T& i2) -> decltype(i1+i2)
{
return(i1+i2);
}
int main(int argc, char* argv[])
{
cout << adder(INT_MAX-10, INT_MAX-3) << endl; // wrong.
cout << adder<long>(INT_MAX-10, INT_MAX-3) << endl; // correct!!.
return(0);
}
Hence we need perhaps some metaprogramming template technique to derive the return type as long if T is int.
没那么简单。
如果 T
是 int
,您不确定 long
是否足够。
标准只这么说
1) int
(sizeof(int) * CHAR_BIT
) 的位数是 至少 16
2) long
(sizeof(long) * CHAR_BIT
) 的位数是 至少 32
3) sizeof(int) <= sizeof(long)
因此,如果编译器用 sizeof(int) == sizeof(long)
管理 int
,这是完全合法的并且
adder<long>(INT_MAX-10, INT_MAX-3);
不起作用,因为 long
可能不足以包含(没有溢出)两个 int
之间的总和。
我没有看到一个简单而优雅的解决方案。
我认为最好的是基于 C++11 引入了以下类型这一事实
1) std::int_least8_t
, 至少8位的最小整数类型
2) std::int_least16_t
, 至少16位的最小整数类型
3) std::int_least32_t
, 至少32位的最小整数类型
4) std::int_least64_t
, 至少64位的最小整数类型
C++11 还引入了 std::intmax_t
作为最大宽度整数类型。
所以我提出以下模板类型选择器
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
即,给定位数,定义相应的最小 "at least" 整数类型。
我还提出以下建议using
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
给定类型 T
,检测肯定包含两个 T
值之和的最小整数类型(位数至少为位数的整数T
加一)。
所以你的 adder()
就变成了
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
观察到 returned 值不只是
return i1 + i2;
否则你 return 类型正确但值错误:i1 + i2
计算为 T
值,因此你可能会溢出,然后将总和分配给 typeNext<T>
变量。
为避免此问题,您必须使用两个值 (typeNext<T>{i1}
) 之一初始化 typeNext<T>
临时变量,然后添加另一个 (typeNext<T>{i1} + i2
) 以获得 typeNext<T>
值,最后是 return 计算值。这样,总和计算为 typeNext<T>
总和,并且您没有溢出。
下面是一个完整的编译示例
#include <cstdint>
#include <climits>
#include <iostream>
#include <type_traits>
template <std::size_t N, typename = std::true_type>
struct typeFor;
/* in case std::intmax_t is bigger than 64 bits */
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool,
(N > 64u) && (N <= sizeof(std::intmax_t)*CHAR_BIT)>>
{ using type = std::intmax_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 32u) && (N <= 64u)>>
{ using type = std::int_least64_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 16u) && (N <= 32u)>>
{ using type = std::int_least32_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N > 8u) && (N <= 16u)>>
{ using type = std::int_least16_t; };
template <std::size_t N>
struct typeFor<N, std::integral_constant<bool, (N <= 8u)>>
{ using type = std::int_least8_t; };
template <typename T>
using typeNext = typename typeFor<1u+sizeof(T)*CHAR_BIT>::type;
template<typename T>
typeNext<T> adder (T const & i1, T const & i2)
{ return {typeNext<T>{i1} + i2}; }
int main()
{
auto x = adder(INT_MAX-10, INT_MAX-3);
std::cout << "int: " << sizeof(int)*CHAR_BIT << std::endl;
std::cout << "long: " << sizeof(long)*CHAR_BIT << std::endl;
std::cout << "x: " << sizeof(x)*CHAR_BIT << std::endl;
std::cout << std::is_same<long, decltype(x)>::value << std::endl;
}
在我的 Linux 64 位平台中,int
是 32 位,long
是 64 位,x
也是 long
和 decltype(x)
是同一类型。
但这对我的平台来说是正确的;没有什么可以保证 long
和 decltype(x)
永远相同。
还要注意尝试获取两个 std::intmax_t
的总和的类型
std::intmax_t y {};
auto z = adder(y, y);
给出错误并且无法编译,因为没有为比 sizeof(std::intmax_t)*CHAR_BIT
更大的 N
定义 typeFor
。