Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use lambda as template argument with default value in C++? [duplicate]

Tags:

c++

lambda

c++17

I want to do a simple thing:

void DoUntil(auto predicate = [] { return false; });

Obviously this doesn't work - I have to use a template argument:

template <typename P>
void DoUntil(P predicate = [] { return false; });

But this statement doesn't work either - the Clang gives an error:

error: no matching function for call to …
note: candidate template ignored: couldn't infer template argument 'P'

If I do call the function without arguments, somehow compiler fails to deduce type from a default argument:

int main() { DoUntil(); }

I don't want to use std::function<> in any way.

Are there any other possible solutions to my problem?

like image 644
abyss.7 Avatar asked Feb 13 '17 14:02

abyss.7


2 Answers

A lambda is an anonymous type with no default constructor (of cause, you can use its copy/move constructor if available). If you must go the lambda way, you can do:

namespace detail{ auto predicate = [] { return false; }; }

template <typename P = decltype(detail::predicate)>
void DoUntil(P pred = detail::predicate);

Rather than trying to fiddle around with lambdas. You can go the good old way:

namespace detail{
    struct DefaultPredicate{ bool operator()() const { return false; } };
}

template <typename P = detail::DefaultPredicate>
void DoUntil(P predicate = P{});

Or better still as Kyle Strand answered.

like image 41
WhiZTiM Avatar answered Nov 08 '22 04:11

WhiZTiM


Use function overloading instead of the default argument feature. Create a non-template function that takes no arguments in addition to the template function:

void DoUntil() ;

template <typename P>
void DoUntil(P predicate) ;

The no-argument version can simply invoke the template version with the lambda you want to use as the default predicate:

void DoUntil() { DoUntil([] { return false; }); }

The problem with your original approach is that you're trying to provide a default template specialization by specifying a default argument value, but without specifying a default template-type. Even without involving lambdas, the following won't work because T doesn't have a default type, even though t has a default value:

template <typename T>
void Foo(T t = 3);

What's needed is to specify a default type for T using <typename T = int>.

As noted in WhiZTiM's answer, the default type for the case involving the lambda function must be deduced using decltype. This is of course because lambdas have unique types known only to the compiler.

like image 100
Kyle Strand Avatar answered Nov 08 '22 05:11

Kyle Strand