What is the most efficient way to pass a non generic function?
Clash Royale CLAN TAG#URR8PPP
up vote
11
down vote
favorite
I am learning functional programming in C++. My intention is to pass a non generic function as argument. I know about the template method, however I would like to restrict the function signature as part of the API design. I worked out 4 different methods example on cpp.sh:
// Example program
#include <iostream>
#include <string>
#include <functional>
typedef int(functor_type)(int);
int by_forwarding(functor_type &&x)
return x(1);
int functor_by_value(functor_type x)
return x(1);
int std_func_by_value(std::function<functor_type> x)
return x(1);
int std_func_by_forwarding(std::function<functor_type> &&x)
return x(1);
int main()
std::cout << functor_by_value((int a)return a;); // works
std::cout << std_func_by_value((int a)return a;); // works
std::cout << std_func_by_forwarding(std::move((int a)return a;)); // works
//std::cout << by_forwarding((int a)return a;); // how to move lambda with forwarding ?
Is any of the above attempts correct? If not, how do i achieve my goal?
c++ function functor std-function
 |Â
show 3 more comments
up vote
11
down vote
favorite
I am learning functional programming in C++. My intention is to pass a non generic function as argument. I know about the template method, however I would like to restrict the function signature as part of the API design. I worked out 4 different methods example on cpp.sh:
// Example program
#include <iostream>
#include <string>
#include <functional>
typedef int(functor_type)(int);
int by_forwarding(functor_type &&x)
return x(1);
int functor_by_value(functor_type x)
return x(1);
int std_func_by_value(std::function<functor_type> x)
return x(1);
int std_func_by_forwarding(std::function<functor_type> &&x)
return x(1);
int main()
std::cout << functor_by_value((int a)return a;); // works
std::cout << std_func_by_value((int a)return a;); // works
std::cout << std_func_by_forwarding(std::move((int a)return a;)); // works
//std::cout << by_forwarding((int a)return a;); // how to move lambda with forwarding ?
Is any of the above attempts correct? If not, how do i achieve my goal?
c++ function functor std-function
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!
– hellow
Aug 16 at 9:13
5
Short answer:int functor_by_value(functor_type x)
.
– George
Aug 16 at 9:15
std::invoke
if you have access to C++17
– Madden
Aug 16 at 9:17
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloadedoperator ()
. So everyx
has anoperator ()
so usual calling syntaxx(1);
works, however none of them are functions.
– VTT
Aug 16 at 9:20
 |Â
show 3 more comments
up vote
11
down vote
favorite
up vote
11
down vote
favorite
I am learning functional programming in C++. My intention is to pass a non generic function as argument. I know about the template method, however I would like to restrict the function signature as part of the API design. I worked out 4 different methods example on cpp.sh:
// Example program
#include <iostream>
#include <string>
#include <functional>
typedef int(functor_type)(int);
int by_forwarding(functor_type &&x)
return x(1);
int functor_by_value(functor_type x)
return x(1);
int std_func_by_value(std::function<functor_type> x)
return x(1);
int std_func_by_forwarding(std::function<functor_type> &&x)
return x(1);
int main()
std::cout << functor_by_value((int a)return a;); // works
std::cout << std_func_by_value((int a)return a;); // works
std::cout << std_func_by_forwarding(std::move((int a)return a;)); // works
//std::cout << by_forwarding((int a)return a;); // how to move lambda with forwarding ?
Is any of the above attempts correct? If not, how do i achieve my goal?
c++ function functor std-function
I am learning functional programming in C++. My intention is to pass a non generic function as argument. I know about the template method, however I would like to restrict the function signature as part of the API design. I worked out 4 different methods example on cpp.sh:
// Example program
#include <iostream>
#include <string>
#include <functional>
typedef int(functor_type)(int);
int by_forwarding(functor_type &&x)
return x(1);
int functor_by_value(functor_type x)
return x(1);
int std_func_by_value(std::function<functor_type> x)
return x(1);
int std_func_by_forwarding(std::function<functor_type> &&x)
return x(1);
int main()
std::cout << functor_by_value((int a)return a;); // works
std::cout << std_func_by_value((int a)return a;); // works
std::cout << std_func_by_forwarding(std::move((int a)return a;)); // works
//std::cout << by_forwarding((int a)return a;); // how to move lambda with forwarding ?
Is any of the above attempts correct? If not, how do i achieve my goal?
c++ function functor std-function
asked Aug 16 at 9:11


Afoxinabox
827
827
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!
– hellow
Aug 16 at 9:13
5
Short answer:int functor_by_value(functor_type x)
.
– George
Aug 16 at 9:15
std::invoke
if you have access to C++17
– Madden
Aug 16 at 9:17
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloadedoperator ()
. So everyx
has anoperator ()
so usual calling syntaxx(1);
works, however none of them are functions.
– VTT
Aug 16 at 9:20
 |Â
show 3 more comments
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!
– hellow
Aug 16 at 9:13
5
Short answer:int functor_by_value(functor_type x)
.
– George
Aug 16 at 9:15
std::invoke
if you have access to C++17
– Madden
Aug 16 at 9:17
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloadedoperator ()
. So everyx
has anoperator ()
so usual calling syntaxx(1);
works, however none of them are functions.
– VTT
Aug 16 at 9:20
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!– hellow
Aug 16 at 9:13
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!– hellow
Aug 16 at 9:13
5
5
Short answer:
int functor_by_value(functor_type x)
.– George
Aug 16 at 9:15
Short answer:
int functor_by_value(functor_type x)
.– George
Aug 16 at 9:15
std::invoke
if you have access to C++17– Madden
Aug 16 at 9:17
std::invoke
if you have access to C++17– Madden
Aug 16 at 9:17
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloaded
operator ()
. So every x
has an operator ()
so usual calling syntax x(1);
works, however none of them are functions.– VTT
Aug 16 at 9:20
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloaded
operator ()
. So every x
has an operator ()
so usual calling syntax x(1);
works, however none of them are functions.– VTT
Aug 16 at 9:20
 |Â
show 3 more comments
4 Answers
4
active
oldest
votes
up vote
12
down vote
accepted
(based on clarification from comments)
Signature can be restricted by using std::is_invocable
:
template<typename x_Action> auto
functor_by_value(x_Action && action)
static_assert(std::is_invocable_r_v<int, x_Action, int>);
return action(1);
online compiler
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
add a comment |Â
up vote
4
down vote
Other alternative:
template <typename Func>
auto functor_by_value(Func&& f)
-> decltype(std::forward<Func>(f)(1))
return std::forward<Func>(f)(1);
add a comment |Â
up vote
4
down vote
however I would like to restrict the function signature as part of the API design.
So restrict it:
#include <functional>
#include <type_traits>
#include <iostream>
/// @tparam F is a type which is callable, accepting an int and returning an int
template
<
class F,
std::enable_if_t
<
std::is_convertible_v<F, std::function<int(int)>>
>* = nullptr
>
int myfunc(F &&x)
return x(1);
int main()
auto a = myfunc((int x) std::cout << x << std::endl; return 1; );
// does not compile
// auto b = myfunc(() std::cout << "foo" << std::endl; return 1; );
2
This would fail when object can be invoked asint (int)
but can not be converted tostd::function
object. For example when object is not copyable / movable but haspublic: int operator ()(int)
.
– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
add a comment |Â
up vote
4
down vote
As usual, this depends on how good your compiler is today, and how good it will be in the future.
Currently, compilers are not very good at optimizing std::function
. Surprisingly, std::function
is a complicated object that sometimes has to allocate memory to maintain stateful lambda functions. It also complicates matters that std::function
has to be able to refer to member function, regular functions, and lambdas, and do it in a transparent manner. This transparency has a hefty runtime cost.
So, if you want the fastest possible code, you should be careful with std::function
. For that reason the first variant is the fastest (on today's compilers):
int functor_by_value(functor_type x)
return x(1);
It simply passes a pointer to a function.
When stateful lambdas are involved you have only two options. Either pass the lambda as a template argument, or convert to std::function
. Hence, if you want the fastest code possible with lambdas (in today's compilers), you'd pass the function as a templated argument.
Since a lambda function may have a big state, passing it around may copy the big state (when copy elision is not possible). GCC will construct the lambda directly on the parameter list (with no copy), but a nested function will invoke a copy constructor for the lambda. To avoid that, either pass it by const reference (in that case it can't be mutable), or by rvalue reference:
template<class Func>
void run2(const Func & f)
std::cout << "Runningn";
f();
template<class Func>
void run(const Func & f)
run2(f);
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Or:
template<class Func>
void run2(Func && f)
f();
template<class Func>
void run(Func && f)
run2(std::forward<Func>(f));
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Without using references, the BigState() will be copied when the lambda is copied.
UPDATE:
After reading the question again I see that it wants to restrict the signature
template<typename Func,
typename = std::enable_if_t<
std::is_convertible_v<decltype(Func(1)), int>>>
void run2(const Func & f)
std::cout << "Runningn";
f();
This will restrict it to any function that can accept int
(possibly with an implicit cast), and returns an int
or any type that is implicitly cast to int
. However, if you want to accept only function-like objects that accept exactly int
and return exactly int you can see if the lambda is convertible to std::function<int(int)>
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
12
down vote
accepted
(based on clarification from comments)
Signature can be restricted by using std::is_invocable
:
template<typename x_Action> auto
functor_by_value(x_Action && action)
static_assert(std::is_invocable_r_v<int, x_Action, int>);
return action(1);
online compiler
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
add a comment |Â
up vote
12
down vote
accepted
(based on clarification from comments)
Signature can be restricted by using std::is_invocable
:
template<typename x_Action> auto
functor_by_value(x_Action && action)
static_assert(std::is_invocable_r_v<int, x_Action, int>);
return action(1);
online compiler
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
add a comment |Â
up vote
12
down vote
accepted
up vote
12
down vote
accepted
(based on clarification from comments)
Signature can be restricted by using std::is_invocable
:
template<typename x_Action> auto
functor_by_value(x_Action && action)
static_assert(std::is_invocable_r_v<int, x_Action, int>);
return action(1);
online compiler
(based on clarification from comments)
Signature can be restricted by using std::is_invocable
:
template<typename x_Action> auto
functor_by_value(x_Action && action)
static_assert(std::is_invocable_r_v<int, x_Action, int>);
return action(1);
online compiler
edited Aug 16 at 9:52
answered Aug 16 at 9:45


VTT
21.1k32143
21.1k32143
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
add a comment |Â
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
@lubgr Just a habit. I've been working in environment that prohibits implicit object construction.
– VTT
Aug 16 at 9:54
add a comment |Â
up vote
4
down vote
Other alternative:
template <typename Func>
auto functor_by_value(Func&& f)
-> decltype(std::forward<Func>(f)(1))
return std::forward<Func>(f)(1);
add a comment |Â
up vote
4
down vote
Other alternative:
template <typename Func>
auto functor_by_value(Func&& f)
-> decltype(std::forward<Func>(f)(1))
return std::forward<Func>(f)(1);
add a comment |Â
up vote
4
down vote
up vote
4
down vote
Other alternative:
template <typename Func>
auto functor_by_value(Func&& f)
-> decltype(std::forward<Func>(f)(1))
return std::forward<Func>(f)(1);
Other alternative:
template <typename Func>
auto functor_by_value(Func&& f)
-> decltype(std::forward<Func>(f)(1))
return std::forward<Func>(f)(1);
answered Aug 16 at 9:31
Jarod42
106k1295166
106k1295166
add a comment |Â
add a comment |Â
up vote
4
down vote
however I would like to restrict the function signature as part of the API design.
So restrict it:
#include <functional>
#include <type_traits>
#include <iostream>
/// @tparam F is a type which is callable, accepting an int and returning an int
template
<
class F,
std::enable_if_t
<
std::is_convertible_v<F, std::function<int(int)>>
>* = nullptr
>
int myfunc(F &&x)
return x(1);
int main()
auto a = myfunc((int x) std::cout << x << std::endl; return 1; );
// does not compile
// auto b = myfunc(() std::cout << "foo" << std::endl; return 1; );
2
This would fail when object can be invoked asint (int)
but can not be converted tostd::function
object. For example when object is not copyable / movable but haspublic: int operator ()(int)
.
– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
add a comment |Â
up vote
4
down vote
however I would like to restrict the function signature as part of the API design.
So restrict it:
#include <functional>
#include <type_traits>
#include <iostream>
/// @tparam F is a type which is callable, accepting an int and returning an int
template
<
class F,
std::enable_if_t
<
std::is_convertible_v<F, std::function<int(int)>>
>* = nullptr
>
int myfunc(F &&x)
return x(1);
int main()
auto a = myfunc((int x) std::cout << x << std::endl; return 1; );
// does not compile
// auto b = myfunc(() std::cout << "foo" << std::endl; return 1; );
2
This would fail when object can be invoked asint (int)
but can not be converted tostd::function
object. For example when object is not copyable / movable but haspublic: int operator ()(int)
.
– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
add a comment |Â
up vote
4
down vote
up vote
4
down vote
however I would like to restrict the function signature as part of the API design.
So restrict it:
#include <functional>
#include <type_traits>
#include <iostream>
/// @tparam F is a type which is callable, accepting an int and returning an int
template
<
class F,
std::enable_if_t
<
std::is_convertible_v<F, std::function<int(int)>>
>* = nullptr
>
int myfunc(F &&x)
return x(1);
int main()
auto a = myfunc((int x) std::cout << x << std::endl; return 1; );
// does not compile
// auto b = myfunc(() std::cout << "foo" << std::endl; return 1; );
however I would like to restrict the function signature as part of the API design.
So restrict it:
#include <functional>
#include <type_traits>
#include <iostream>
/// @tparam F is a type which is callable, accepting an int and returning an int
template
<
class F,
std::enable_if_t
<
std::is_convertible_v<F, std::function<int(int)>>
>* = nullptr
>
int myfunc(F &&x)
return x(1);
int main()
auto a = myfunc((int x) std::cout << x << std::endl; return 1; );
// does not compile
// auto b = myfunc(() std::cout << "foo" << std::endl; return 1; );
answered Aug 16 at 9:50
Richard Hodges
53.6k55597
53.6k55597
2
This would fail when object can be invoked asint (int)
but can not be converted tostd::function
object. For example when object is not copyable / movable but haspublic: int operator ()(int)
.
– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
add a comment |Â
2
This would fail when object can be invoked asint (int)
but can not be converted tostd::function
object. For example when object is not copyable / movable but haspublic: int operator ()(int)
.
– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
2
2
This would fail when object can be invoked as
int (int)
but can not be converted to std::function
object. For example when object is not copyable / movable but has public: int operator ()(int)
.– VTT
Aug 16 at 10:00
This would fail when object can be invoked as
int (int)
but can not be converted to std::function
object. For example when object is not copyable / movable but has public: int operator ()(int)
.– VTT
Aug 16 at 10:00
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
@VTT I do agree - std::function expects function objects to be callable on the const interface, which may or may not be a bug in the standard, depending on how you look at it. However, the OP mentioned that std::function was a candidate for the argument type, so I took a lead from there. I saw your answer (and upvoted). I was not aware of the is_invocable_r_t type trait.
– Richard Hodges
Aug 16 at 11:24
add a comment |Â
up vote
4
down vote
As usual, this depends on how good your compiler is today, and how good it will be in the future.
Currently, compilers are not very good at optimizing std::function
. Surprisingly, std::function
is a complicated object that sometimes has to allocate memory to maintain stateful lambda functions. It also complicates matters that std::function
has to be able to refer to member function, regular functions, and lambdas, and do it in a transparent manner. This transparency has a hefty runtime cost.
So, if you want the fastest possible code, you should be careful with std::function
. For that reason the first variant is the fastest (on today's compilers):
int functor_by_value(functor_type x)
return x(1);
It simply passes a pointer to a function.
When stateful lambdas are involved you have only two options. Either pass the lambda as a template argument, or convert to std::function
. Hence, if you want the fastest code possible with lambdas (in today's compilers), you'd pass the function as a templated argument.
Since a lambda function may have a big state, passing it around may copy the big state (when copy elision is not possible). GCC will construct the lambda directly on the parameter list (with no copy), but a nested function will invoke a copy constructor for the lambda. To avoid that, either pass it by const reference (in that case it can't be mutable), or by rvalue reference:
template<class Func>
void run2(const Func & f)
std::cout << "Runningn";
f();
template<class Func>
void run(const Func & f)
run2(f);
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Or:
template<class Func>
void run2(Func && f)
f();
template<class Func>
void run(Func && f)
run2(std::forward<Func>(f));
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Without using references, the BigState() will be copied when the lambda is copied.
UPDATE:
After reading the question again I see that it wants to restrict the signature
template<typename Func,
typename = std::enable_if_t<
std::is_convertible_v<decltype(Func(1)), int>>>
void run2(const Func & f)
std::cout << "Runningn";
f();
This will restrict it to any function that can accept int
(possibly with an implicit cast), and returns an int
or any type that is implicitly cast to int
. However, if you want to accept only function-like objects that accept exactly int
and return exactly int you can see if the lambda is convertible to std::function<int(int)>
add a comment |Â
up vote
4
down vote
As usual, this depends on how good your compiler is today, and how good it will be in the future.
Currently, compilers are not very good at optimizing std::function
. Surprisingly, std::function
is a complicated object that sometimes has to allocate memory to maintain stateful lambda functions. It also complicates matters that std::function
has to be able to refer to member function, regular functions, and lambdas, and do it in a transparent manner. This transparency has a hefty runtime cost.
So, if you want the fastest possible code, you should be careful with std::function
. For that reason the first variant is the fastest (on today's compilers):
int functor_by_value(functor_type x)
return x(1);
It simply passes a pointer to a function.
When stateful lambdas are involved you have only two options. Either pass the lambda as a template argument, or convert to std::function
. Hence, if you want the fastest code possible with lambdas (in today's compilers), you'd pass the function as a templated argument.
Since a lambda function may have a big state, passing it around may copy the big state (when copy elision is not possible). GCC will construct the lambda directly on the parameter list (with no copy), but a nested function will invoke a copy constructor for the lambda. To avoid that, either pass it by const reference (in that case it can't be mutable), or by rvalue reference:
template<class Func>
void run2(const Func & f)
std::cout << "Runningn";
f();
template<class Func>
void run(const Func & f)
run2(f);
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Or:
template<class Func>
void run2(Func && f)
f();
template<class Func>
void run(Func && f)
run2(std::forward<Func>(f));
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Without using references, the BigState() will be copied when the lambda is copied.
UPDATE:
After reading the question again I see that it wants to restrict the signature
template<typename Func,
typename = std::enable_if_t<
std::is_convertible_v<decltype(Func(1)), int>>>
void run2(const Func & f)
std::cout << "Runningn";
f();
This will restrict it to any function that can accept int
(possibly with an implicit cast), and returns an int
or any type that is implicitly cast to int
. However, if you want to accept only function-like objects that accept exactly int
and return exactly int you can see if the lambda is convertible to std::function<int(int)>
add a comment |Â
up vote
4
down vote
up vote
4
down vote
As usual, this depends on how good your compiler is today, and how good it will be in the future.
Currently, compilers are not very good at optimizing std::function
. Surprisingly, std::function
is a complicated object that sometimes has to allocate memory to maintain stateful lambda functions. It also complicates matters that std::function
has to be able to refer to member function, regular functions, and lambdas, and do it in a transparent manner. This transparency has a hefty runtime cost.
So, if you want the fastest possible code, you should be careful with std::function
. For that reason the first variant is the fastest (on today's compilers):
int functor_by_value(functor_type x)
return x(1);
It simply passes a pointer to a function.
When stateful lambdas are involved you have only two options. Either pass the lambda as a template argument, or convert to std::function
. Hence, if you want the fastest code possible with lambdas (in today's compilers), you'd pass the function as a templated argument.
Since a lambda function may have a big state, passing it around may copy the big state (when copy elision is not possible). GCC will construct the lambda directly on the parameter list (with no copy), but a nested function will invoke a copy constructor for the lambda. To avoid that, either pass it by const reference (in that case it can't be mutable), or by rvalue reference:
template<class Func>
void run2(const Func & f)
std::cout << "Runningn";
f();
template<class Func>
void run(const Func & f)
run2(f);
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Or:
template<class Func>
void run2(Func && f)
f();
template<class Func>
void run(Func && f)
run2(std::forward<Func>(f));
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Without using references, the BigState() will be copied when the lambda is copied.
UPDATE:
After reading the question again I see that it wants to restrict the signature
template<typename Func,
typename = std::enable_if_t<
std::is_convertible_v<decltype(Func(1)), int>>>
void run2(const Func & f)
std::cout << "Runningn";
f();
This will restrict it to any function that can accept int
(possibly with an implicit cast), and returns an int
or any type that is implicitly cast to int
. However, if you want to accept only function-like objects that accept exactly int
and return exactly int you can see if the lambda is convertible to std::function<int(int)>
As usual, this depends on how good your compiler is today, and how good it will be in the future.
Currently, compilers are not very good at optimizing std::function
. Surprisingly, std::function
is a complicated object that sometimes has to allocate memory to maintain stateful lambda functions. It also complicates matters that std::function
has to be able to refer to member function, regular functions, and lambdas, and do it in a transparent manner. This transparency has a hefty runtime cost.
So, if you want the fastest possible code, you should be careful with std::function
. For that reason the first variant is the fastest (on today's compilers):
int functor_by_value(functor_type x)
return x(1);
It simply passes a pointer to a function.
When stateful lambdas are involved you have only two options. Either pass the lambda as a template argument, or convert to std::function
. Hence, if you want the fastest code possible with lambdas (in today's compilers), you'd pass the function as a templated argument.
Since a lambda function may have a big state, passing it around may copy the big state (when copy elision is not possible). GCC will construct the lambda directly on the parameter list (with no copy), but a nested function will invoke a copy constructor for the lambda. To avoid that, either pass it by const reference (in that case it can't be mutable), or by rvalue reference:
template<class Func>
void run2(const Func & f)
std::cout << "Runningn";
f();
template<class Func>
void run(const Func & f)
run2(f);
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Or:
template<class Func>
void run2(Func && f)
f();
template<class Func>
void run(Func && f)
run2(std::forward<Func>(f));
int main()
run([s=BigState()]() std::cout << "applyn"; );
return 0;
Without using references, the BigState() will be copied when the lambda is copied.
UPDATE:
After reading the question again I see that it wants to restrict the signature
template<typename Func,
typename = std::enable_if_t<
std::is_convertible_v<decltype(Func(1)), int>>>
void run2(const Func & f)
std::cout << "Runningn";
f();
This will restrict it to any function that can accept int
(possibly with an implicit cast), and returns an int
or any type that is implicitly cast to int
. However, if you want to accept only function-like objects that accept exactly int
and return exactly int you can see if the lambda is convertible to std::function<int(int)>
edited Aug 21 at 11:53
answered Aug 16 at 9:37


Michael Veksler
1,320411
1,320411
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51873464%2fwhat-is-the-most-efficient-way-to-pass-a-non-generic-function%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
prog.cc:29:41: warning: moving a temporary object prevents copy elision
:) clang warnings ftw!– hellow
Aug 16 at 9:13
5
Short answer:
int functor_by_value(functor_type x)
.– George
Aug 16 at 9:15
std::invoke
if you have access to C++17– Madden
Aug 16 at 9:17
Lambdas are anonymous, therefore you can only grab its type through type deduction.
– Passer By
Aug 16 at 9:18
It depends on meaning you put into "pass a non generic function" First two variants accept a reference or a pointer to function. 3rd and 4th variant accept an object or an rvalue reference to object with overloaded
operator ()
. So everyx
has anoperator ()
so usual calling syntaxx(1);
works, however none of them are functions.– VTT
Aug 16 at 9:20