Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default lambda as templated parameter of a function

Consider the following code

template<bool b, typename T> void foo(const T& t = []() {}) {
  // implementation here
}

void bar() {
  foo<true>([&](){ /* implementation here */ }); // this compiles
  foo<true>(); // this doesn't compile
}

In the case that doesn't compile I get the following errors:

error C2672: 'foo': no matching overloaded function found
error C2783: 'void foo(const T&)': could not deduce template argument for 'T'

I think it's clear what I want to achieve: let foo be called with and without a client-provided lambda. The compiler is MSVC++2017 version 15.4.4 toolset v141.

like image 379
Serge Rogatch Avatar asked May 31 '26 06:05

Serge Rogatch


2 Answers

Default function arguments are not part of the template argument deduction process. To quote [temp.deduct.partial]/3:

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • In the context of a function call, the types used are those function parameter types for which the function call has arguments. 141

141) Default arguments are not considered to be arguments in this context; they only become arguments after a function has been selected.

That bullet and note indicate that since you didn't provide an argument for t in the call to foo, the type T cannot be deduced. The default lambda argument can only be taken into account if the function is selected to be called, not before.

The solution, as all the others have noted, is to provide an overload without parameters, that will call the templated one with the default lambda you have in mind.

like image 88
StoryTeller - Unslander Monica Avatar answered Jun 01 '26 19:06

StoryTeller - Unslander Monica


Another (very efficient) way - default T to be a null functor.

// no_op is a function object which does nothing, regardless of how many
// arguments you give it. It will be elided completely unless you compile with
// -O0
struct no_op 
{ 
    template<class...Args>
    constexpr void operator()(Args&&...) const {} 
};

// foo defaults to using a default-constructed no_op as its function object
template<bool b, typename T = no_op> void foo(T&& t = T()) 
{    
  // implementation here
    t();
}

void bar() {
  foo<true>([&](){ std::cout << "something\n"; }); // this compiles
  foo<true>(); // this now compiles
}
like image 38
Richard Hodges Avatar answered Jun 01 '26 20:06

Richard Hodges