Using STL algorithms on unary functions - c++

So I often want to perform some STL algorithm on a range of elements and instead of a comparison function, I would like to pass a unary function f.
For example I would like to write something like this
std::max_element(begin(range), end(range), f);
to find the maximum element in the range after applying f.
My current workaround looks something like that:
std::max_element(begin(range), end(range, [&f](auto a, auto b){ return f(a) < f(b); });
At first glance, this may look like no problem. But f could be a lambda expression itself or in another way more complicate than just f.
I have two problem with that piece of code:
a) It is error prone because one could accidently write f(a) < f(a) (especially if more complicated and one used copy and past). This is the problem of code duplication
b) It does not express the intent very well. If I want to sort by a function, I do not want to deal with a comparison.
Unfortunately I have not found a good solution to this kind of problem in the standard library (or even boost), so I would like to ask you what your solution to this problem is. Is there any reason for the non-existence of this overload in the algorithms?

Using c++ 20's ranges you can do:
std::ranges::max_element(range | std::views::transform(f));

One thing you can do is create your own generic comparator that accepts a unary function to perform a transform:
// Generic comparator
template<typename T, typename F>
struct transform_comparator_type
{
transform_comparator_type(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};
// Helper function to deduce the type of F
template<typename T, typename F>
auto transform_comparator(F f)
{
return transform_comparator_type<T, F>(f);
}
int main()
{
std::vector<int> v{1, 4, 3, 6, 0};
auto e = std::max_element(std::begin(v), std::end(v),
transform_comparator<int>([v](int i){ return 2 * i; }));
// etc...
}
Edited to add:
The type can actually be deduced from the return type of the supplied transform function, so you don't need the helper function. You can do this instead:
template<typename F>
struct transform_comparator
{
using T = decltype(F()({})); // deduce T from return type of F
transform_comparator(F f): f(f) {}
bool operator()(T const& a, T const& b) const { return f(a) < f(b); }
F f;
};

Related

What's the least verbose way to hint a overloaded function template as template parameter

I'm trying to create a function template which accepts std::max and others with compatible prototypes:
template <typename F>
int f(F g)
{
return g(1,2);
}
Because of the many overloads, the template argument F cannot be inferred f.e. for std::max, so this will fail:
f(std::max);
One possibility is to hint with a static_cast:
f(static_cast<int const& (*)(int const&,int const&)>(std::max));
I'm looking for a less verbose way while still being able to also easily pass any other sort of matching function, like f.e.
f([](int i, int j) { return 3; });
f(std::function<int(int,int)>([](int i, int j) { return 4; }));
Here's a godbolt.
If there's an easy way for C++11 and C++14 this would be great but if there's only one for C++17 and above this would also help a lot.
The issue is std::max not your function.
There is no way you could modify your function to make it accept a set of overloads. A set of overloads is just not something you can pass along. What you can pass along is an object with overloaded operator(). For example
struct foo {
template <typename T> bool operator()(T a,T b){return a+b;}
};
f(foo{}); // OK
std::max however is a function template and you need to instantiate the function before the function can be passed to f. Nothing you can do about that. Though you not necessarily need std::function or static_cast to pick the right overload. As mentioned in a comment you can do
f([](int a, int b) { return std::max(a, b); });
This triggers instantiation of std::max<int> inside the lambda, or if you prefer to select the type inside f you can do similar as in the above example:
f([](auto a, auto b) { return std::max(a,b); });
This lambda expression is similar to the above foo. You have a concrete object and only the operator() is a template which only gets instantiated when called, such that in f you could even do:
template <typename F>
int f(F g)
{
if (some_condition) return g(1,2); // calls std::max<int>
else return g(1.2,1.4); // calls std::max<double>
// (though int is returned anyhow)
}
f([](auto a, auto b) { return std::max(a,b); });

Can a std::function typedef be used to help define a lambda?

Let's say a I have a typedef for a specific std::function such as:
typedef std::function<int(int a, int b, int c)> func_type;
Can I reuse that typedef when defining a lambda that implements it?
For example, in the example below, the function foo accepts a func_type, but the call-site for foo needs to replicate the signature:
void foo(func_type f) {
// ...
}
int main() {
foo([](int a, int b, int c){ return a + b + c; });
}
Can I somehow re-use the func_type typedef when declaring the lambda, so I don't have to repeat the argument list (and so changes to the func_type typedef will be transparent for lambda bodies that work with the new definition).
Something like [](??? func_type ???){ return a + b + c; }.
The variable names in std::function<int(int a, int b, int c)> are not part of the type -- they are basically comments. There is no way to extract them at any other point.
So if you hope to get a, b and c you are out of luck.
One simple thing you can do is use auto:
foo( [](auto...args) { return args+...+0; } );
which is close to what you want. If you have 3 arguments you can do:
foo( [](auto a, auto b, auto c) { return a+b+c; } );
But the return type doesn't match, other than because std::function does the conversion for you.
You can extract the types of a b and c and make the lambda work differently, but not with the return type. Not unless you do something insane like:
template<class T>
struct tag_t{ contexpr tag_t(){} using type=T; };
template<class T>
constexprt tag_t<T> tag{};
template<class Tag>
using type_t = typename Tag::type;
template<class F>
struct deducer {
F f;
template<class R, class...Args>
operator std::function<R(Args...)>() const {
return f( tag<R>, tag<Args>... );
}
};
template<class F>
deducer<F> make_deducer( F f ){ return {std::move(f)}; }
int main() {
foo(make_deducer([](auto R, auto...Args){
return []( type_t<decltype(Args)>... args )->type_t<decltype(R)> {
return 0+...args;
});
}));
}
I would advise against this. But I deduced the argument types and return type of the lambda from what std::function I was passed to.
What we do here is we create a deducer type, that when converted to a std::function passes the arguments and return type expected to a lambda it stores. That lambda then generates a custom lambda for those exact arguments.
This is neither brief, simple nor sane.
If you know you have a std::function and what you want to do is defer the selection of the type arguments to std::function, you can just have a generic lambda:
foo([](auto... xs) { return (... + xs); });
Since it's std::function's call operator that drives how the lambda is called, this'll do the right thing. Of course, this requires C++14 (and the fold-expression I used above requires C++17, but that's not as important). You may or may not want to use auto&&, depending on what the types actually are.
For C++11, you can't easily do such a thing with a lambda. You'd need to fix the arity and manually list all the types. This isn't practical. You could fallback to using a normal function object, with a call operator template, but then you lose the advantages of a lambda.
A std::function is
is a general-purpose polymorphic function wrapper. Instances of
std::function can store, copy, and invoke any Callable target --
functions, lambda expressions, bind expressions, or other function
objects, as well as pointers to member functions and pointers to data
members. -- source cppreference.com
So yes, this approach is perfectly valid !
However the signature of the typedef can't be taken over to short-circuit the lambda definition.
Remark: The typedef is about the return type and the parameter types but not the parameter names, so if short-circuiting the parameter list would be legal, the body of the lambda would not know which parameters to use:
int main() {
foo([](int d, int e, int f){ return d + e + f; });
}

Is it possible to allow one std::function type accept lambdas with different signatures

I have a higher order function map which is similar to STL for_each, and maps a std::function object over a vector of things.
template<class T, class U>
vector<U> map(function<U (T)> f, vector<T> xs) {
vector<U> ret;
for (auto &x: xs)
ret.push_back(f(x));
return ret;
}
Now, I want to have this higher order function take both objects of types function<int (const vector<T>&)> and function<int (vector<T>)>, as shown in the attached minimal example.
The problem is that function<int (const vector<T>&)> and function<int (vector<T>)> seem to be convertible to each other (see head and head2), but map won't take the const references version function<int (const vector<int>&)> (see Q1).
It is possible to tell map to accept the const reference version with explicit conversion (Q2), but this is rather cumbersome.
I was wondering if, in general, it is possible to write a function deref that removes the const reference from function<int (const vector<T>&)> and returns a function<int (vector<T>)>?
(If above is possible, then I won't have to write two identical overloads/implementations of map for const refs).
Thanks.
#include <vector>
#include <functional>
using namespace std;
template<class T, class U>
vector<U> map(function<U (T)> f, vector<T> xs) {
vector<U> ret;
for (auto &x: xs)
ret.push_back(f(x));
return ret;
}
int main() {
vector<vector<int>> m;
function<int (const vector<int>&)> head = [](const vector<int>& a) {return a[0];};
function<int (const vector<int>&)> head1 = [](vector<int> a) {return a[0];}; //conversion OK
function<int (vector<int>)> head2 = [](const vector<int>& a) {return a[0];}; //conversion OK
map(head2,m); //OK
map(head,m); //Q1: problem line, implicit conversion NOT OK
map(function<int (vector<int>)>(head),m); //Q2: explicit conversion OK
map(deref(head),m); //Q3: ??How-to, deref takes a std::function f and returns a function with const ref removed from its signature
return 0;
}
--- EDIT ---
I am particularly interested in a deref like function or a meta-function that can remove the const ref from the type signature of a std::function object, so that I can at least do Q2 automatically.
I know that, as #Brian and #Manu correctly pointed out, the use of std::function to specify types is not conventional, but I wonder what I asked above is even feasible. Personally, I think code with std::function has greater clarity, considering how generic function types Func<T1, T2, T3, ...,Tn, Tresult> are used in C#. This is if the cost of type erasure is tolerable.
I fully agree that c++ can infer return types and give an error message when type is wrong. Maybe it's just a matter of taste and I would prefer to spell it out when writing function signatures.
I understand why you are using std::function: You have to know the return type of the transformation to create the vector, right?
But consider a completely different approach. Given the metafunction std::result_of you could compute the result type of a function call, so just write:
template<typename F , typename CONTAINER , typename T = typename std::result_of<F(typename CONTAINER::value_type)>::type>
std::vector<T> map( F f , CONTAINER&& container )
{
std::vector<T> result;
for( auto& e : container )
result.emplace_back( f( e ) );
return result;
}
Advantages:
No abuse of std::function: Always think what std::function does (i.e. type erasure), don't use it as an universal function type.
Rely on duck typing instead of coupling on the types: Don't worry, if something was wrong it wouldn't compile neither.
Works for any Standard Library Container since we extracted the element type with the value_type trait, instead of using std::vector directly.
The code is much more clear and efficient, both because the reduction of std::function usage.
Regarding the question "Its possible to write a function that accepts lambdas of multiple signatures?"
Using std::function you could write something similar to Boost.OverloadedFunction in a couple of lines:
template<typename F , typename... Fs>
struct overloaded_function : public std_function<F> , public std_function<Fs>...
{
overloaded_function( F&& f , Fs&&... fs ) :
std_function<F>{ f },
std_function<Fs>{ fs }...
{}
};
Where std_function is a metafunction which given a function type F returns the std::function instance with the signature of F. I leave it as a game/challenge for the reader.
Thats all. Improve it with a make-like function:
template<typename F , typename... Fs>
overloaded_function<F,Fs...> make_overloaded_function( F&& f , Fs&&... fs )
{
return { std::forward<F>( f ) , std::forward<Fs>( fs )... };
}
And you are ready to go:
auto f = make_overloaded_function( [](){ return 1; } ,
[](int,int){ return 2; } ,
[](const char*){ return 3; } );
f(); //Returns 1
f(1,2); //Returns 2
f("hello"); //Returns 3
EDIT: "Thanks. But, what I am really looking for, is a meta-function that takes the signature of a callable, and removes the const refs from the signature."
Ok, let me try: The std::decay metafunction applies the decaying done when passing argumments by value to a given type. This includes removing cv qualifiers, removing references, etc. So a metafunction like yours could be something that takes a function signature type and applies decaying to all its argumments:
template<typename F>
struct function_decay;
template<typename R typename... ARGS>
struct function_decay<R(ARGS...)>
{
using type = R(typename std::decay<ARGS>::type...);
};
That should do the work.
I have written this because you explicitly asked for it in the comment, but I strongly encourage you to use the alternative I showed you initially, because it has many advantages compared to your way.
That said, I hope this answer helped to solve your problem.
The idiomatic solution is to simply allow map to take an arbitrary function-like type,
template<class T, class F>
auto map(F f, vector<T> xs) -> vector<typename result_of<F(T)>::type> {
vector<typename result_of<F(T)>::type> ret;
for (auto &x: xs)
ret.push_back(f(x));
return ret;
}
The main issue with this approach is that you get confusing error messages if F is not callable with arguments of type T, or if it returns something strange, like void.
(A secondary issue is that the first argument to map can't be an overloaded function; the compiler won't simply be able to pick the overload that takes an argument of type T.)
(You might also want to consider decaying the return type of f.)

std algorithms with pointer to member as comparator/"key"

I often find myself using std::sort, std::max_element, and the like with a lambda that simply invokes a member function
std::vector<MyType> vec;
// populate...
auto m = std::max_element(std::begin(vec), std::end(vec),
[](const MyType& a, const MyType& b) { return a.val() < b.val()})
this feels like a waste of characters and a loss of clarity. I'm aware that I could write another function/callable and pass a function pointer/callable object to these algorithm functions, but I often need to do this sort-by just once in a program and it doesn't strike me as a good way of addressing the problem. What I want to do, ideally is say:
auto m = std::max_element(std::begin(vec), std::end(vec), &MyType::val);
and have the objects be sorted by their val()s. Is there some part of the stdlib I'm overlooking that could assist me with this? or another simple way of doing it? I'd like to make what this is sorting or searching by as obvious as possible.
I'm aware that just &MyType::val isn't enough, I am looking for something that could perhaps wrap it, or provide a similar functionality without obscurring the meaning.
You can use std::mem_fn (or std::tr1::mem_fn)
int main()
{
std::vector<MyType> vec;
auto m = std::max_element(std::begin(vec), std::end(vec), compare_by(std::mem_fn(&MyType::field)));
}
Of course, this assumes that you have a utility like compare_by in your toolbox (as you should :)):
template <typename F>
struct CompareBy {
explicit CompareBy(F&& f) : f(std::forward<F>(f)) {}
template <typename U, typename V>
bool operator()(U const& u, V const& v) const {
return f(u) < f(v);
}
private:
F f;
};
template <typename F>
CompareBy<F> compare_by(F&& f) { return CompareBy<F>(std::forward<F>(f)); }
See it Live On Coliru
You can do it without introducing any new functions (templated or not).
Just use bind and std::less
auto m = std::max_element(vec.begin(), vec.end(),
bind(less<>(), bind(&MyType::val, _1), bind(&MyType::val, _2)));
A templated comparator could help you:
template <typename StructureType,
typename MemberType,
MemberType StructureType::*member>
bool comparator(const StructureType& the_first, const StructureType& the_second)
{
return the_first.*member < the_second.*member;
}
http://ideone.com/K8ytav
A bit of type traits magic could certainly allows you to avoid writing the type.
How about once overloading operator< for your custom type? This can be naturally done inside the class (or directly next to it), and then no further argument is required beside the iterators.
Passing your val() function isn't possible, as you must pass a binary operator.
EDIT: Having read the other valuable alternatives (also sehe's nice response), I want to confirm what I already mentioned in the comment below: In my opinion, nothing beats the readibility, locality and also flexibility of a lambda expression (--on the risk of writing some passages twice).
#Ryan Haining: I suggest you to keep it as in your original post.
An improvement on sehes answer, so as to avoid needing to use std::mem_fn would be provide a pointer to member overload for the compare_by function.
example usage
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field));
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field, std::greater<>{}));
the code to implement
#include <functional> // std::less
#include <utility> // std::move
#include <type_traits> // std::is_invocable_r
// Forward declaration
template<typename R, typename T, typename F = std::less<R>>
auto compare_by(R T::*, F = F{});
// Implementation
namespace detail {
template<typename T, typename F>
struct compare_by_t;
template<typename R, typename T, typename F>
struct compare_by_t<R T::*, F> : private F
{
compare_by_t(F&& f, R T::*m): F{std::move(f)}, _member{m} {}
R T::* _member;
bool operator()(T const& x, T const& y) const
{
return F::operator()(x .* _member, y .* _member);
}
};
} // detail
template<typename R, typename T, typename F>
auto compare_by(R T::* member, F f)
{
static_assert(std::is_invocable_r<bool, F, R, R>::value);
return detail::compare_by_t<R T::*, F>{ std::move(f), member };
}

How can I pass a lambda (c++11) into a templated function?

I'm playing around with lambda functions in gcc 4.6.2, and would like to implement a templated "map" function like this:
template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) {
std::vector<B> rv;
rv.resize(orig.size());
std::transform(begin(orig), end(orig), begin(rv), f);
return rv;
}
This doesn't work, because the test code:
int main(int argc, char **argv) {
std::vector<int> list;
list.push_back(10);
list.push_back(20);
list.push_back(50);
std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; });
std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); });
return 0;
}
gives this error:
test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’
test.cpp:49:80: note: candidate is:
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>)
If I remove the templating, and use a vector directly, it compiles fine:
std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) {
std::vector<int> rv;
rv.resize(orig.size());
std::transform(begin(orig), end(orig), begin(rv), f);
return rv;
}
so it must be a problem with the way I'm defining the template.
Has anyone run into this before? I know lambdas are incredibly new.
You don't need to use std::function. Just make the predicate parameter a template value. For example,
template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) {
std::function<> is more useful as a member value type or for defining non-templated code.
The problem is that the compiler can't figure out what to use for B. In order to determine that type it wants to use the function<> you pass in for f, but you don't pass an std::function<> directly. You pass in something you expect to be used to construct a function<>. And in order to do that implicit construction it needs to know the type of argument. So you've got this circular dependency where the type of argument depends on what you pass in, but what gets passed in depends on the type of argument.
You can break this circular dependency by specifying the template parameters, such as map_<int,int>(list, [](int x) -> char { return x + 1; });
(although I see the functor actually returns a char, not an int, so if the type deduction worked for you here you'd be getting back a vector<char> which cannot be converted to a vector<int> when you assign the result to transformed)
However as has been pointed out, generally templates take functors as just a plain template type:
template<typename A,typename Func>
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> {
std::vector<decltype(f(A()))> rv;
/*...*/
}
(we use the trailing return type because we need to use the expression f in the return type, which isn't available unless the return type comes afterwards.)
This allows the template to deduce the functor type directly and avoids any type conversions and best allows for optimization.
It's also customary to use iterators as arguments on these sorts of functions, in which case your function is just a wrapper around std::transform, so you can just use that directly. I'm not sure there's a whole lot of value in a special version that deals with vectors specifically.
I'm tackling with lambdas too and i noticed that you can declare a function pointer in a function definition's parameter list and when you make a call to that function you can pass a lambda expression as an argument if it matches the function prototype of course.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
template <typename T,typename C>
struct map {
typedef C (*F)(const T&);
std::vector<C> rv;
map () {}
map (const std::vector<T>& o,F f) {
rv.resize(o.size());
std::transform (o.begin(),o.end(),rv.begin(),f);
}
~map () {}
operator std::vector<C> () const {
return rv;
}
};
int main () {
std::vector<int> asd(5,12);
std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;});
std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," "));
}