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.