我在看Jason Turner的C++ Weekly - Ep 64 - C++11's std::min (and my version)
现在我开始在这里进行追逐,使用 std::common_type
使参数包扩展适用于多种类型:
template <typename T, typename U>
const typename std::common_type<T, U>::type&
multi_type_min(const T& t, const U& u)
{
return t < u ? t : u;
}
template<typename First, typename ...T>
typename std::common_type< First, T...>::type
variadic_min( const First& f, const T& ...t )
{
const typename std::common_type< First, T...>::type* retVal =&f;
(( retVal= &multi_type_min(*retVal, t)), ...);
return *retVal;
}
我如何实现这一目标?我做了一些愚蠢的事情here吗?
答案 0 :(得分:3)
如果您使用警告进行编译,则会发现您的错误:
<source>:7:11: warning: returning reference to local temporary object [-Wreturn-stack-address]
return t < u ? t : u;
^~~~~~~~~~~~~
<source>:15:17: note: in instantiation of function template specialization 'multi_type_min<float, unsigned int>' requested here
(( retVal= &multi_type_min(*retVal, t)), ...);
^
<source>:28:14: note: in instantiation of function template specialization 'variadic_min<float, unsigned int, unsigned int, unsigned int>' requested here
return variadic_min( z, a,b,c );
^
1 warning generated.
因此,您有未定义的行为。由于T
和U
可能不是同一类型,因此您无法同时使用它们的公共引用/指针。你需要:
multi_type_min
返回一个值,而不是引用。retVal
成为值,而不是指针。Demo:
#include <type_traits>
template <typename T, typename U>
const typename std::common_type<T, U>::type
multi_type_min(const T& t, const U& u)
{
return t < u ? t : u;
}
template<typename First, typename ...T>
typename std::common_type< First, T...>::type
variadic_min( const First& f, const T& ...t )
{
typename std::common_type< First, T...>::type retVal = f;
(( retVal= multi_type_min(retVal, t)), ...);
return retVal;
}
int main()
{
unsigned int a=8, b= 2, c=4;
float z = 43.42f;
return variadic_min( z, a,b,c );
}
这是一个替代实现,在根据常见类型创建初始化列表后委托给std::min
:
template<typename First, typename ...T>
constexpr
typename std::common_type<First, T...>::type
common_min(const First& f, const T& ...t) {
std::initializer_list<typename std::common_type<First, T...>::type> ilist = {
static_cast<typename std::common_type<First, T...>::type>(f),
static_cast<typename std::common_type<First, T...>::type>(t)...
};
return std::min(ilist);
}
答案 1 :(得分:3)
使用std::common_type
实现的可变参数std :: min不是一个好主意。
将无符号类型与已签名类型进行比较会发生什么?您是否希望签名的未签名,或者未签名的签名?如果我们将有符号整数转换为无符号整数,我们就有可能卷入一个非常大的正值。如果我们将无符号整数转换为有符号整数,我们就有可能溢出。
当您递归执行与std::common_type
的比较时,序列的std::common_type
可能与两个相邻对之间的std::common_type
不同。
例如,
auto res = variadic_min(1, -211, 3, -63, 89u);
std::common_type
的{{1}}为1, -211, 3, -63, 89u
BUT
unsigned int
的{{1}}为std::common_type
,因此当您比较它们时,您将获得1, -211
作为最低要求。比较int
和-211
时也是如此。但是,当您比较-211, 3
时,-211, -63
为-211 89u
,因此std::common_type
变为4294967085,因此unsigned int
是最小值,这当然是荒谬的因为它既不是最小的也不是最大的;只是整数推广的受害者。
如果你想在所有类型中统一使用-211
,那么你可能会这样做:
89u
然后至少你不会受到整体晋升的伤害;所有类型都转换为common_type
,因此输出至少是可预测的;你所有的负数都会变成大正数:
输出:
template<class T, class U>
std::common_type_t<T, U>
variadic_min(const T& t, const U& u)
{
if (t < u)
std::cout << t << " less than " << u << std::endl;
else
std::cout << u << " less than " << t << std::endl;
return t < u ? t : u;
}
template<class First, class Second, class... Rest>
std::common_type_t<First, Second, Rest...>
variadic_min(const First& f, const Second& s, const Rest& ...t )
{
using ret_t = std::common_type_t<First, Second, Rest...>;
return variadic_min(variadic_min(static_cast<ret_t>(f), static_cast<ret_t>(s)), static_cast<ret_t>(t)...);
}
最后一种更好的方法可能是寻找未签名/签名的比较,然后在这种情况下做一些特别的事情。例如。首先检查签名值是否为零,如果它更小,则返回该值,而不是将其转换为大的正值。