I'm trying to make a template function to which is possible to pass some other function with any type and number of parameters and bind it to a std::function
. I managed to do this:
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return bar;
}
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
return f;
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void (int)>(foo));
bar (0); // prints 0
}
I would like to just call auto barp = func(foo);
and have the types deduced, but this line gives the following compilation errors:
error: no matching function for call to ‘func(void (&)(int))’
auto barp = func(foo);
^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
^
note: template argument deduction/substitution failed:
note: mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
auto barp = func(foo);
^
Why is it trying to match std::function<_Res(_ArgTypes ...)>
with int (*)(int)
? I feel I should get the compiler somehow to expand _Res(_ArgTypes ...)
to int(int)
, but how?
A function is not an std::function
, it is convertible to one. You can deduce the arguments of a function, however, barring ambiguity about overloads.
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
// Will cause error.
//int foo(double);
template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
return f;
}
int main()
{
auto bar = func(foo);
bar (0); // prints 0
}
What you want to do with the original std::function
is similar to this, which more obviously does not work:
template<typename T>
struct A
{
A(T);
};
template<typename T>
void func(A<T> a);
int main()
{
func(42);
}
42
is not a A
, it can be converted to one, though. However, converting it to one would require T
to already be known.
Your code is semantically equivalent to (if it compiled) this:
int foo(int x){
std::cout << x << std::endl;
return x;
}
int main(){
auto bar = [](int x){ return foo(x); };
bar(0);
}
Apart from the return x
part, but that's just me correcting your undefined behaviour.
Your function for returning an std::function
is extremely unnecessary, except maybe for the sake of less typing.
You could just as easily use std::function
s constructor without the wrapper function.
But, still.
To do what you want to do, you should pass the function pointer itself; without impossible conversion.
try this:
int foo(int x){
return x + 1;
}
template<typename Ret, typename ... Args>
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{
return {fp};
}
int main(){
auto bar = func(foo);
std::cout << bar(0) << std::endl; // outputs '1'
}
The reason your code doesn't work is because of the implicit conversion trying to take place when you pass an argument to func
.
As I said, your code currently is semantically equivalent to the example I showed above using lambda expressions. I would strongly recommend just using lambda expressions wherever function wrapping is needed! They're much more flexible and are a core part of the language rather than a library feature.
Remember, non-capturing lambdas are convertible to function pointers; so the following is conforming:
int main(){
int(*bar)(int) = [](int x){ return x + 1; };
std::cout << bar(0) << std::endl;
}
and to have similar functionality as you want in your post, we could write something like this:
int main(){
auto func = +[](int x){ return x + 1; };
std::cout << "foo(5) = " << func(5) << std::endl;
func = [](int x){ return x * 2; };
std::cout << "bar(5) = " << func(5) << std::endl;
}
Notice we don't mess around with function pointer declarations or library types? Much nicer for everybody to read/write. One thing to notice in this example is the unary +
operator; to perform a conversion to function pointer before assigning it to the variable. It actually looks very functional , which seems to be what you're trying to achieve here.
Try to unwrap the functor (lambda and std::function) through its operator()
:
#include <iostream>
#include <functional>
int foo(int bar)
{
std::cout << bar << std::endl;
return 0;
}
template<typename /*Fn*/>
struct function_maker;
template<typename RTy, typename... ATy>
struct function_maker<RTy(ATy...)>
{
template<typename T>
static std::function<RTy(ATy...)> make_function(T&& fn)
{
return std::function<RTy(ATy...)>(std::forward<T>(fn));
}
};
template<typename /*Fn*/>
struct unwrap;
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...) const>
: function_maker<RTy(ATy...)> { };
template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...)>
: function_maker<RTy(ATy...)> { };
template<typename T>
auto func(T f)
-> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>()))
{
return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f));
}
int main()
{
//auto barp = func(foo); // compilation error
auto bar = func(std::function<void(int)>(foo));
auto bar2 = func([](int)
{
// ...
});
bar(0); // prints 0
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.