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.
It is true that a lambda can be assigned to a std::function, but that is not its native type. We’ll talk about what that means soon. As a matter of fact, there is no standard type for lambdas. A lambda’s type is implementation defined, and the only way to capture a lambda with no conversion is by using auto:
This means that calling a lambda many times (such as with std::sort or std::copy_if) is much better than using a global function. This is one example of where C++ is actually faster than C. std::function is a templated object that is used to store and call any callable type, such as functions, objects, lambdas and the result of std::bind.
Because they are objects rather than pointers they can be inlined very easily by the compiler, much like functors. This means that calling a lambda many times (such as with std::sort or std::copy_if) is much better than using a global function. This is one example of where C++ is actually faster than C.
Lambdas are a fancy name for anonymous functions. Essentially they are an easy way to write functions (such as callbacks) in the logical place they should be in the code. My favorite expression in C++ is [] () {} ();, which declares an empty lambda and immediately executes it. It is of course completely useless. Better examples are with STL, like:
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;
}
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.
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
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.
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