In C++03 the following code works fine:
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::vector<int> v2;
v2.push_back(2);
v2.push_back(3);
v2.push_back(4);
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), std::max<int>);
return 0;
}
In C++11 this doesn't work because it added an overload for std::max
that contains an initializer_list
. Therefore, you have to use a very ugly cast to choose the correct overload:
static_cast<const int& (*)(const int&, const int&)>(std::max)
I have a few questions.
If you are doing this sufficiently frequently, you might want to write a transparent functor wrapper:
struct my_max {
template<class T>
const T& operator()(const T& a, const T& b) const{
return std::max(a, b);
}
};
Then you can simply do
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(), my_max());
whenever you need it, rather than writing a lambda or a cast each time. This is basically the same idea as the transparent operator functors - let the template arguments be deduced at the actual call site rather than explicitly specified when you create the functor.
If you want to make this fancier, you can even have operator()
take heterogeneous types and add perfect forwarding and use trailing return types:
struct my_max {
template<class T, class U>
constexpr auto operator()( T&& t, U&& u ) const
-> decltype(t < u ? std::forward<U>(u) : std::forward<T>(t)){
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
In C++14, this is simplified to
struct my_max {
template<class T, class U>
constexpr decltype(auto) operator()( T&& t, U&& u ) const{
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
What is a workaround?
A lambda is probably the most readable and useful for predicates and comparators:
std::transform(v.begin(), v.end(), v2.begin(), v2.begin(),
[] (int a, int b) {return std::max(a,b);} );
You might want to check out T.C.s functor if you need it more often. Or, with C++14:
auto max = [] (auto&& a, auto&& b) -> decltype(auto)
{return a > b? std::forward<decltype(a)>(a) : std::forward<decltype(b)>(b);};
Why did the standard committee decide to do this knowing it would (probably) break existing code and force the user to create an ugly cast?
The only explanation is that they found the new overload to bring enough joy to compensate the breaking of existing code and the need for workarounds in future.
You could just use std::max_element
instead of this new overload, so you trade the syntax sugar for passing std::max
-specializations as predicates for the syntax sugar of finding the maximum element within a couple of variables without explicitly creating an array for it.
Basically
std::transform( ..., std::max<int> );
// <=>
std::transform( ..., [] (int a, int b) {return std::max(a,b);} );
vs
int arr[] {a,b,c,d}; // You don't have an array with a,b,c,d included consecutively yet
int maximum = *std::max_element( std::begin(arr), std::end(arr) ); // ensure arr non-empty!
// <=>
auto maximum = std::max({a, b, c, d});
Maybe it does compensate? On the other hand, you barely ever need the latter.
Are future standards of C++ going to attempt to alleviate this problem?
I don't think so. Apparently, the standard committee really doesn't like to remove recently introduced features. I don't really see that much of a problem either; The lambda does the job.
Although I've accepted T.C's answer which provides a comprehensive breakdown, as stated in a comment, I want to mimic the transparent comparator functor for class templates like std::less
. This answer is provided for critique by others incase there's anything wrong with the syntax.
template <typename T = void>
struct my_max;
template <>
struct my_max<void> {
template<class T, class U>
constexpr decltype(auto) operator()( T&& t, U&& u ) const {
return t < u ? std::forward<U>(u) : std::forward<T>(t);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With