std::function vs template
In general, if you are facing a design situation that gives you a choice, use templates. I stressed the word design because I think what you need to focus on is the distinction between the use cases of std::function
and templates, which are pretty different.
In general, the choice of templates is just an instance of a wider principle: try to specify as many constraints as possible at compile-time. The rationale is simple: if you can catch an error, or a type mismatch, even before your program is generated, you won't ship a buggy program to your customer.
Moreover, as you correctly pointed out, calls to template functions are resolved statically (i.e. at compile time), so the compiler has all the necessary information to optimize and possibly inline the code (which would not be possible if the call were performed through a vtable).
Yes, it is true that template support is not perfect, and C++11 is still lacking a support for concepts; however, I don't see how std::function
would save you in that respect. std::function
is not an alternative to templates, but rather a tool for design situations where templates cannot be used.
One such use case arises when you need to resolve a call at run-time by invoking a callable object that adheres to a specific signature, but whose concrete type is unknown at compile-time. This is typically the case when you have a collection of callbacks of potentially different types, but which you need to invoke uniformly; the type and number of the registered callbacks is determined at run-time based on the state of your program and the application logic. Some of those callbacks could be functors, some could be plain functions, some could be the result of binding other functions to certain arguments.
std::function
and std::bind
also offer a natural idiom for enabling functional programming in C++, where functions are treated as objects and get naturally curried and combined to generate other functions. Although this kind of combination can be achieved with templates as well, a similar design situation normally comes together with use cases that require to determine the type of the combined callable objects at run-time.
Finally, there are other situations where std::function
is unavoidable, e.g. if you want to write recursive lambdas; however, these restrictions are more dictated by technological limitations than by conceptual distinctions I believe.
To sum up, focus on design and try to understand what are the conceptual use cases for these two constructs. If you put them into comparison the way you did, you are forcing them into an arena they likely don't belong to.
std::function vs callable as template parameter
Even though you specified <uint32_t>
as a template argument, the compiler seems to try to deduce more elements for the parameter pack, fails to do so (because the type of a lambda is not std::function<...>
), and becomes upset.
You need to somehow inhibit template argument deduction.
Either call it as exec0<uint32_t>({_g}, {_h});
, or wrap parameter types in std::type_identity_t<...>
(or, pre-C++20, std::enable_if_t<true, ...>
).
Then the compiler will accept your uint32_t
as the only type in the pack, and won't try to add more types.
how to use std::function to point to a function template
No. A template function is exactly that, a template. It's not a real function. You can point a std::function to a specific instantiation of the template function, e.g. func<int,int>
std::function performance as compared to templates
When I compile your program (with -O3
optimization) and use calc1
, the execution time is 0.0 seconds. This is because the compiler can completely optimize away the code. It knows your code doesn't actually do anything, so there's no sense in running any of it.
When I compile your program (again with -O3
optimization) and use calc2
(which uses std::function
), the program takes 2 seconds to run. The reason it takes longer is because the optimizer can't optimize everything away. std::function
works at runtime (not compile time, because it has to do type erasure; see this question and this question), and in general the optimizer can't inline (or entirely optimize away) calls that go through std::function
(in this situation, it's technically possible for the optimizer to do so, since this is a simple program, but it doesn't).
The reason std::function
calls can't be inlined is because the compiler doesn't always know what std::function
will do. In this code, it's simple enough that the compiler's static analyzer could, if it was "smart" enough, actually inline the whole thing and then optimize it away.
But that can be a tricky thing to implement in a compiler, and it doesn't make a very big difference in "real" programs that are more complex. In more complex programs, it can actually be impossible to know what std::function
will do. For example, imagine you have a second .cpp
file that calls calc2
with a different std::function
. Or imagine if you set your std::function
to one of two different lambdas, depending on user input. The compiler wouldn't know which lambda to actually call until the program ran, so it couldn't just optimize everything away. Because of issues like this, it's not really worth the effort of implementing deep static analysis for std::function
that would completely optimize away your simple code.
Template functors vs functions
There are two main reasons: The first is, as pythonic metaphor noted, partial specialization is only valid for classes and not functions. Note that functions can use overloads to overcome this problem generally, but often if you are doing metaprogramming it's easier and more generic to use partial specialization. I'd actually think this was the main reason.
The second reason is that anytime that code wants to accept a function object (like in the STL, e.g. std::transform), it will have a type template parameter. If you pass a functor or a lambda, the exact type is known at compile time, and you don't pay for indirection, and inlining can be performed. If you pass a function pointer (or a std::function), only the signature is known at compile time, and you pay for an indirection (and you can't inline). For instance, std::sort can be considerably faster with a functor than a function pointer.
Note that there is a little used feature called function pointer template parameters; these are non type template parameters that specialize on a specific function, and thus can remove indirection. However, if you use one of these, you can't use a functor at all. So most code that wants to accepts a function object does it the way I described above.
Template parameter in std::function
Third argument to function run
is declared to be std::function<double(const T &, const T &)>
, however you pass a pointer to scoreValue1
causing type deduction to be impossible. In order to deal with this problem you need to either
- declare argument to be a pointer to function
void run
(
const std::vector<T> & dataA
, const std::vector<T> & dataB
, double (* function )(const T & a, const T & b)
)
online compiler
- Put
T
of third argument into non-deducible context:
void run
(
const std::vector<T> & dataA
, const std::vector<T> & dataB
, std::function<double(const ::std::type_identity_t<T> &, const ::std::type_identity_t<T> &)> function
)
online compiler
static member std::function of template class gets empty despite initalization
The problem is related to the order of initialization of static variables which I guess is solved differently for the templated instantiated static variables compared to Test<double>
on different compilers.
inline static Holder f;
is a static variable, so somewhere before entering main it will be default initialized (to an empty function). But Test<double>
is another static variable that will get its own initialization before entering main.
On GCC it happens that
Test<double>
is calledTest<double>::f
is set by the constructor ofTest<double>
- the default constructor of
Test<double>::f
is called, thus emptying the function
This all happens inside __static_initialization_and_destruction_0
GCC method, if you actually use a wrapper object to break on static initialization of the variable you can see what's happening: https://onlinegdb.com/UYEJ0hbgg
How could the compiler know that you plan to set a static variable from another static variable before its construction? That's why, as you said, using static variables is a bad practice indeed.
Pass template function to std::bind?
handle
is not a template function. There are no "template functions". handle
is a function template, ie it is a template, it is not a function. You cannot std::bind
to a template. You can only std::bind
to a callable.
The trick is to defer instantiation of the template and deduction of the template parameters to when the function is actually called:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
struct foo {
struct handle_caller {
template <typename T,typename U>
void operator()(foo* f, T t,U u){
f->handle(t,u);
}
};
void f()
{
auto cb = std::bind(handle_caller{},this, placeholders::_1, placeholders::_2);
}
template <typename T, typename U>
void handle(T, U)
{
}
};
int main()
{
return 0;
}
The callable passed to bind is an object of a concrete type handle_caller
. It is not a template. Only when cb
is called the parameters are forwarded to handle_caller::operator()
where the template arguments can be deduced.
Lambdas can do this out-of-the box, because a lambda with auto
arguments is of a concrete type and only its operator()
is a template:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
struct foo {
void f()
{
auto cb = std::bind([](auto f,auto t,auto u){ f->handle(t,u);},this, placeholders::_1, placeholders::_2);
}
template <typename T, typename U>
void handle(T, U)
{
}
};
int main()
{
return 0;
}
However, once you use the lambda there is no need for std::bind
anymore, because you can bind the parameters via a lambda capture. std::bind
is the ancient way to bind parameters, it is convoluted and has clunky syntax. I have read of cases that can be done with std::bind
but not with a lambda, but I have never encountered one.
PS: Note that I removed the shared_from_this
stuff from your code, because I know it can be used wrong easily, but I am not sure how to use it correctly. As cb
is only local to foo::f
there is no need to worry about the lifetime of this
in the example code.
Related Topics
Removing Item from Vector, While in C++11 Range 'For' Loop
How to Increase the Re-Usability of This Key-Oriented Access-Protection Pattern
Lock-Free Progress Guarantees in a Circular Buffer Queue
Copying a Polymorphic Object in C++
Py_Initialize Fails - Unable to Load the File System Codec
Best Way to Store Currency Values in C++
How to Forward Declare an Inner Class
Std::String to Float or Double
Checking Cin Input Stream Produces an Integer
Smart Pointers (Boost) Explained
Std::Vector Versus Std::Array in C++
What Is the Purpose of Std::Launder
Why Are C++ Inline Functions in the Header
Difference Between Conversion Specifiers %I and %D in Formatted Io Functions (*Printf/*Scanf)
Why Is a Pure Virtual Function Initialized by 0