8

I would like to have a default lambda for a functor argument in my function.

I am aware it is possible using a struct and operator() like this:

struct AddOne {
    int operator()(int a) {
        return a+1;
    }
};

template <typename Functor = AddOne>
int run_old(int x, Functor func = AddOne()) 
{
    return func(x);
}

But I was wondering if there was a modern way, given the changes in the standard in either c++14/17/20, to make this work?

template <typename Functor>
int run_new(int x, Functor func = [](int a){ return a+1; }) 
{
    return func(x);
}

I'm not sure what one would use as the default type to Functor, or if there is syntax i'm unaware of.

https://godbolt.org/z/Hs6vQs

max66
  • 65,235
  • 10
  • 71
  • 111
Salgar
  • 7,687
  • 1
  • 25
  • 39
  • "*modern way*" What's wrong with the old way? The struct method is much easier to read at the function site. – Nicol Bolas Sep 04 '19 at 15:11
  • 2
    In the _old way_, you likely don't want to provide both default template and function arguments this way: https://wandbox.org/permlink/PtSwiQwsNDyDrshu. Use `Functor func = Functor()` instead. – Daniel Langr Sep 04 '19 at 15:15

6 Answers6

8

From C++11 you can already do that:

template <typename Functor = int(int)>
int run_new(int x, Functor func = [](int a){ return a+1; }) 
{
    return func(x);
}
BiagioF
  • 9,368
  • 2
  • 26
  • 50
3

Just add an overload for this.

template <typename Functor>
int run_new(int x, Functor func) 
{
    return func(x);
}

int run_new(int x) 
{
    return run_new(x, [](int a){ return a+1; });
}

Allows you to get around not beening able to have a lambda expression as a default function argument.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

Not quite "modern" but you could use plain old overloading with a non-template method taking only a single parameter:

int run_new(int x) 
{
    return func(x,[](int a){ return a+1;});  // calls the template overload
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

Just for fun, in C++20 we have both (1) lambdas in unevaluated contexts and (2) lambdas without capture are default constructible. Combine those two and you get:

template <typename Functor = decltype([](int a){ return a+1; })>
int run_new(int x, Functor func = {})
{
    return func(x);
}

Obligatory godbolt.

Barry
  • 286,269
  • 29
  • 621
  • 977
0

Alternatively to other questions, you can even avoid templates:

int run_new(int x, std::function<int(int)> func = [](int a){ return a + 1; })
{
   return func(x);
}
BiagioF
  • 9,368
  • 2
  • 26
  • 50
Daniel Langr
  • 22,196
  • 3
  • 50
  • 93
  • 5
    Do note that you do incur a cost for this. `std::function` uses type erasure so you have dynamic allocation and more than likely lose the ability to inline the code. – NathanOliver Sep 04 '19 at 15:24
  • 2
    @NathanOliver Both GCC and Clang seem to be smart enough to generate the same inlined assembly code for `f`, which is minimal: https://godbolt.org/z/uuEeNE. Need to admit I was surprised. – Daniel Langr Sep 04 '19 at 15:26
  • 3
    @DanielLangr But now the functor has to be copyable – Artyer Sep 04 '19 at 15:30
  • @Artyer That's the drawback. The advantage is that you don't need to expose the function definition in a header file. – Daniel Langr Sep 04 '19 at 15:32
0

The best I can imagine pass through a variable

static constexpr auto defFunc = [](int a){ return a+1; };

template <typename Functor = decltype(defFunc)>
int run_new(int x, Functor func = defFunc) 
{
    return func(x);
}
max66
  • 65,235
  • 10
  • 71
  • 111