std::min 带参数包
std::min with Parameter Pack
我在看杰森·特纳的 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?
如果你编译时出现警告,你会看到你的错误:
<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
可能不是同一类型,因此它们不能有共同的 reference/pointer。您需要:
- 使
multi_type_min
return 成为一个值,而不是一个引用。
- 使
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);
}
用 std::common_type
实现的可变参数 std::min 不是一个好主意。
将无符号类型与有符号类型进行比较时会发生什么?您希望已签名的变成未签名的,还是应该将未签名的变成已签名的?如果我们将有符号整数转换为无符号整数,我们就有可能回绕到一个非常大的正值。如果我们将无符号整数转换为有符号整数,我们就有溢出的风险。
当您递归地执行与 std::common_type
的比较时,序列的 std::common_type
可能与两个相邻对之间的 std::common_type
不同。
例如,
auto res = variadic_min(1, -211, 3, -63, 89u);
1, -211, 3, -63, 89u
的std::common_type
是unsigned int
但是
1, -211
的std::common_type
是int
,所以比较时,-211
return是最小的。当你比较 -211, 3
和 -211, -63
时也是如此。然而,当你比较-211 89u
时,std::common_type
是unsigned int
,所以-211
变成了4294967085,因此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)...);
}
至少你不会成为整体提升的受害者;您所有的类型都转换为 unsigned int
因此输出至少是可预测的;你所有的负数都会变成大的正数:
输出:
1 less than 4294967085
1 less than 3
1 less than 4294967233
1 less than 89
Res: 1
最终更好的方法可能是留意 unsigned/signed 比较,然后在这种情况下做一些特别的事情。例如。首先检查带符号的值是否为零,如果小于零,则 return 而不是将其转换为较大的正值。
我在看杰森·特纳的 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?
如果你编译时出现警告,你会看到你的错误:
<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
可能不是同一类型,因此它们不能有共同的 reference/pointer。您需要:
- 使
multi_type_min
return 成为一个值,而不是一个引用。 - 使
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);
}
用 std::common_type
实现的可变参数 std::min 不是一个好主意。
将无符号类型与有符号类型进行比较时会发生什么?您希望已签名的变成未签名的,还是应该将未签名的变成已签名的?如果我们将有符号整数转换为无符号整数,我们就有可能回绕到一个非常大的正值。如果我们将无符号整数转换为有符号整数,我们就有溢出的风险。
当您递归地执行与 std::common_type
的比较时,序列的 std::common_type
可能与两个相邻对之间的 std::common_type
不同。
例如,
auto res = variadic_min(1, -211, 3, -63, 89u);
1, -211, 3, -63, 89u
的std::common_type
是unsigned int
但是
1, -211
的std::common_type
是int
,所以比较时,-211
return是最小的。当你比较 -211, 3
和 -211, -63
时也是如此。然而,当你比较-211 89u
时,std::common_type
是unsigned int
,所以-211
变成了4294967085,因此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)...);
}
至少你不会成为整体提升的受害者;您所有的类型都转换为 unsigned int
因此输出至少是可预测的;你所有的负数都会变成大的正数:
输出:
1 less than 4294967085
1 less than 3
1 less than 4294967233
1 less than 89
Res: 1
最终更好的方法可能是留意 unsigned/signed 比较,然后在这种情况下做一些特别的事情。例如。首先检查带符号的值是否为零,如果小于零,则 return 而不是将其转换为较大的正值。