live example
这对参数进行了完美的转发。它依赖于 RVO 的返回值,因为无论输入类型如何,它都会返回一个值类型,因为 common_type 就是这样做的。
我实现了common_type推导,允许传入混合类型,输出“预期”的结果类型。
我们支持 1 个元素的最小值,因为它使代码更流畅。
#include <utility>
#include <type_traits>
template<typename T>
T vmin(T&&t)
{
return std::forward<T>(t);
}
template<typename T0, typename T1, typename... Ts>
typename std::common_type<
T0, T1, Ts...
>::type vmin(T0&& val1, T1&& val2, Ts&&... vs)
{
if (val2 < val1)
return vmin(val2, std::forward<Ts>(vs)...);
else
return vmin(val1, std::forward<Ts>(vs)...);
}
int main()
{
std::cout << vmin(3, 2, 0.9, 2, 5) << std::endl;
std::cout << vmin(3., 1.2, 1.3, 2., 5.2) << std::endl;
return 0;
}
现在,虽然以上是完全可以接受的解决方案,但并不理想。
表达式 ((a<b)?a:b) = 7 是合法的 C++,但 vmin( a, b ) = 7 不是,因为 std::common_type decays 是盲目的参数(由于我认为它在输入两个值时返回右值引用的过度反应 -在 std::common_type 的旧实现中键入)。
简单地使用decltype( true?a:b ) 很诱人,但它会导致右值引用问题,并且不支持common_type 特化(例如std::chrono)。所以我们都想用common_type,又不想用。
其次,写一个min函数,不支持不相关的指针,不让用户改变比较函数似乎是错误的。
所以下面是上面的一个更复杂的版本。 live example:
#include <iostream>
#include <utility>
#include <type_traits>
namespace my_min {
// a common_type that when fed lvalue references all of the same type, returns an lvalue reference all of the same type
// however, it is smart enough to also understand common_type specializations. This works around a quirk
// in the standard, where (true?x:y) is an lvalue reference, while common_type< X, Y >::type is not.
template<typename... Ts>
struct my_common_type;
template<typename T>
struct my_common_type<T>{typedef T type;};
template<typename T0, typename T1, typename... Ts>
struct my_common_type<T0, T1, Ts...> {
typedef typename std::common_type<T0, T1>::type std_type;
// if the types are the same, don't change them, unlike what common_type does:
typedef typename std::conditional< std::is_same< T0, T1 >::value,
T0,
std_type >::type working_type;
// Careful! We do NOT want to return an rvalue reference. Just return T:
typedef typename std::conditional<
std::is_rvalue_reference< working_type >::value,
typename std::decay< working_type >::type,
working_type
>::type common_type_for_first_two;
// TODO: what about Base& and Derived&? Returning a Base& might be the right thing to do.
// on the other hand, that encourages silent slicing. So maybe not.
typedef typename my_common_type< common_type_for_first_two, Ts... >::type type;
};
template<typename... Ts>
using my_common_type_t = typename my_common_type<Ts...>::type;
// not that this returns a value type if t is an rvalue:
template<typename Picker, typename T>
T pick(Picker&& /*unused*/, T&&t)
{
return std::forward<T>(t);
}
// slight optimization would be to make Picker be forward-called at the actual 2-arg case, but I don't care:
template<typename Picker, typename T0, typename T1, typename... Ts>
my_common_type_t< T0, T1, Ts...> pick(Picker&& picker, T0&& val1, T1&& val2, Ts&&... vs)
{
// if picker doesn't prefer 2 over 1, use 1 -- stability!
if (picker(val2, val1))
return pick(std::forward<Picker>(pick), val2, std::forward<Ts>(vs)...);
else
return pick(std::forward<Picker>(pick), val1, std::forward<Ts>(vs)...);
}
// possibly replace with less<void> in C++1y?
struct lesser {
template<typename LHS, typename RHS>
bool operator()( LHS&& lhs, RHS&& rhs ) const {
return std::less< typename std::decay<my_common_type_t<LHS, RHS>>::type >()(
std::forward<LHS>(lhs), std::forward<RHS>(rhs)
);
}
};
// simply forward to the picked_min function with a smart less than functor
// note that we support unrelated pointers!
template<typename... Ts>
auto min( Ts&&... ts )->decltype( pick( lesser(), std::declval<Ts>()... ) )
{
return pick( lesser(), std::forward<Ts>(ts)... );
}
}
int main()
{
int x = 7;
int y = 3;
int z = -1;
my_min::min(x, y, z) = 2;
std::cout << x << "," << y << "," << z << "\n";
std::cout << my_min::min(3, 2, 0.9, 2, 5) << std::endl;
std::cout << my_min::min(3., 1.2, 1.3, 2., 5.2) << std::endl;
return 0;
}
上述实现的缺点是大多数类不支持operator=(T const&)&&=delete - 即,它们不会阻止分配右值,如果min 中的一种类型不支持,这可能会导致意外.基本类型可以。
附注:开始删除您的右值引用operator=s 人。