Functions as First Class Citizens in C++: Passing Lambdas or Functions to Function Templates
A Simple Case
Let’s look at a case where we can change the way a Function Template behaves by passing it various Lambdas as arguments.
#include <vector>
#include <iostream>
template <typename AnyType>
using Vec = std::vector<AnyType>;
template <typename T>
auto print ( Vec<T> const& xs, char endline = '\n' ) -> void
{
for ( auto const& x : xs )
std::cout << x << ' ';
std::cout << endline;
}
template <typename AnyType, typename AnyFunc>
auto fmap( AnyFunc func, Vec<AnyType> const& xs ) -> Vec<AnyType>
{
Vec<AnyType> output{};
for ( auto const& x : xs )
output.push_back( func( x ) );
return output;
}
int main()
{
Vec<int> a = { 1,2,3,4,5,6,7,8,9 };
auto f = []( auto& x ){ return x * x; };
auto b = fmap( f, a );
print( b );
// b == Vec<int>{ 1,4,9,16,25,36,49,64,81 }
}
This works well, but what if we have already declared a function?
// ...
auto div_by_2( int const& x ) -> double
{
return static_cast<double>( x ) / 2.0;
}
int main()
{
Vec<int> a = { 1,2,3,4,5,6,7,8,9 };
auto f = []( auto& x ){ return x * x; };
auto b = fmap( f, a );
print( b );
// b == Vec<int>{ 1,4,9,16,25,36,49,64,81 }
auto c = fmap( div_by_2, a );
print( c );
// c == Vec<int>{ 0,1,1,2,2,3,3,4,4 }
}
Uh Oh. It looks like we need fmap to have an output type that differs from its input type.
template <typename InputType, typename OutputType, typename AnyFunc>
auto fmap( AnyFunc func, Vec<InputType> const& xs ) -> Vec<OutputType>
{
Vec<OutputType> output{};
for ( auto const& x : xs )
output.push_back( func( x ) );
return output;
}
But this gives us an compilation error:
error: no matching function for call to 'fmap(main()::<lambda(auto:1&)>&, Vec<int>&)'
auto b = fmap( f, a );
This is remedied by passing the type arguments to the template.
int main()
{
Vec<int> a = { 1,2,3,4,5,6,7,8,9 };
auto f = []( auto& x ){ return x * x; };
auto b = fmap<int, int>( f, a );
print( b );
// b == Vec<int>{ 1,4,9,16,25,36,49,64,81 }
auto c = fmap<int, double>( div_by_2, a );
print( c );
// c == Vec<double>{ 0.5,1,1.5,2,2.5,3,3.5,4,4.5 }
}
Excellent! But now what if our function is a Function Template.
template <typename AnyType>
auto div_by_2( AnyType const& x ) -> double
{
return x / 2.0;
}
int main()
{
Vec<int> a = { 1,2,3,4,5,6,7,8,9 };
auto f = []( auto& x ){ return x * x; };
auto b = fmap<int, int>( f, a );
print( b );
// b == Vec<int>{ 1,4,9,16,25,36,49,64,81 }
auto c = fmap<int, double>( div_by_2, a );
// Compilation Error
}
And the error is:
error: no matching function call to fmap<int, double>(<unresolved overloaded function type>, Vec<int>&);
auto c = fmap<int, double>( div_by_2, a );
Lucky for us, all we need to do is tell the compiler the type of div_by_2.
int main()
{
Vec<int> a = { 1,2,3,4,5,6,7,8,9 };
auto f = []( auto& x ){ return x * x; };
auto b = fmap<int, int>( f, a );
print( b );
// b == Vec<int>{ 1,4,9,16,25,36,49,64,81 }
auto c = fmap<int, double>( div_by_2<int>, a );
print( c );
// c == Vec<double>{ 0.5,1,1.5,2,2.5,3,3.5,4,4.5 }
}
And that’s all there is to it! No need to deal with std::function. If you found this interesting I would highly recommend the Haskell Programming Language.