I am trying to call a particular member function in all elements of a tuple using std::apply
. This works when I use a lambda but not when defining a free function.
Following Template tuple - calling a function on each element the following code works when using a lambda:
#include<tuple>
#include<iostream>
struct Double {
void print() { std::cout << "Double!" << std::endl;}
};
struct Vector {
void print() { std::cout << "Vector!" << std::endl;}
};
template<class ...T>
void printAll2(T &...x){(x.print(), ...);}
int main() {
auto workspace = std::make_tuple<Vector, Double, Vector>({},{},{});
auto printAll = [](auto &...x){(x.print(), ...);};
std::apply(printAll , workspace);
// This does not work.
//std::apply(printAll2, workspace);
return 0;
}
However, when I try to use the free function printAll2
I get an error:
<source>:22:5: error: no matching function for call to 'apply'
22 | std::apply(printAll2, workspace);
| ^~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/ tuple:2955:5: note: candidate template ignored: couldn't infer template argument '_Fn'
2955 | apply(_Fn&& __f, _Tuple&& __t)
| ^
1 error generated.
Compiler returned: 1
What is the difference between the lambda and the function?
Many thanks!
The description of std::apply
(from the documentation):
Invoke the Callable object f with the elements of t as arguments.
(emphasys is mine)
The difference between printAll
and printAll2
:
printAll2
is not a function, but a function template.
A specific function is instantiated by the compiler based on it when you use it with specific arguments. But by itself is is not concrete and is not something that you can apply
(it is not a callable object).
In order to apply it you have to specify the template arguments, thus making it a concrete function (which is a callable object):
//-------------------vvvvvvvvvvvvvvvvvvvvvv--------------
std::apply(printAll2<Vector, Double, Vector>, workspace);
printAll
on the other hand is a generic lambda.
It is therefore an instance of an unnamed concrete class in which only operator()
is templated.
The generic nature of the lambda is taking effect when the lambda is invoked. At that point the compiler instantiates a specific operator()
with the relevant parameters.
Internally this class is implemented along the lines of this example:
class __some_internal_class_name__ {
public:
template<class ...T>
void operator()(T &...x) const {(x.print(), ...);}
};
Since printAll
is a object of that concrete class it can be passed to std::apply
.
Summary:
std::apply
requires a callable object. Such an object must have a specific concrete type (which printAll
is, but printAll2
is not).