Why is this an issue?
To configure an algorithm with a function in C++, you can use one of the following techniques:
- A function pointer (see {rule:cpp:S5205} that explains why it is a bad idea)
- An
std::function
- A template argument
How do you select between an std::function
and a template argument?
std::function
offers the most flexibility. You can store them in a variable, in a container (as std::map<string,
std::function<void(void)>>
for instance… This flexibility is provided by type erasure: A single std::function
can wrap
any kind of functor, as long as the signature is compatible. It also comes with a cost: Due to this type erasure, a compiler will typically not be
able to inline a call to a std::function
.
Template parameters, on the other hand, are less flexible. Each functor has its own type, which prevents storing several of them together even if
they all have compatible signatures. But since each template instance knows the type of the functor, calls can be inlined making this a zero-cost
abstraction.
As a conclusion, if the functor can be known at compile-time, you should prefer using a template parameter, if it has to be dynamic,
std::function
will give you greater flexibility.
This rule detects function parameters of type std::function
that would probably benefit from being replaced by a template parameter.
It does so by looking if the functor is only called inside the function, or if it participates in other operations.
Noncompliant code example
using Criterion = std::function<bool(DataPoint const&)>;
void filter(DataSet* data, Criterion criterion) { // Noncompliant
for (auto &dataPoint : data) {
if (criterion(dataPoint)) {
data.markForRemoval(dataPoint);
}
}
}
Compliant solution
template<class Criterion>
void filter(DataSet* data, Criterion criterion) { // Compliant
for (auto &dataPoint : data) {
if (criterion(dataPoint)) {
data.markForRemoval(dataPoint);
}
}
}
Exceptions
This rule ignores virtual functions, that don’t work well with templates.
Resources