There is no real reason that lambdas can't have default arguments. However, there are two main ways to use lambdas, and only one of them would allow default arguments without changing the type system.

  1. You can call the lambda directly, or through a template. Default parameters would work fine in this case.

  2. You can call the lambda through std::function. Default parameters would not work without changing the type system.

My guess is that new functions that people write in C++11 will usually take std::function parameters because this way, the function won't have to be a template, or it won't have to be instantiated for every single lambda and functor that gets passed to it.

Why can't a std::function (or function pointer) take defaults?

It's not obvious what the type of such a function would be.

  • If it's std::function<int(bool)>, then how do you call it with the defaults? (You can't.)

  • If it's std::function<int(bool=false)>, then what types is it compatible with, and how does conversion work? Can you convert it to std::function<int()>? What about std::function<int(bool=true)>?

  • If it's something new like std::function<int(bool=default)>, then what types is it compatible with, and how does conversion work?

Basically, this isn't just a switch you can flip in the standard and make function pointers / std::function handle default arguments. Default arguments in normal functions are handled using information from the function's declaration, which is not available at the call site for a lambda or function pointer. So you would have to encode information about the defaults into the function type, and then work out all of the non-obvious rules for conversion and compatibility.

So you would have to come up with a compelling case for why such a feature would be added, and convince the committee.

So, why can't lambdas take defaults?

I haven't answered this question. But I don't think it would be a very useful feature to add. I would delete this answer if I could, but it's been accepted. I would downvote it if I could, but it's mine. C'est la vie.

Answer from Dietrich Epp on Stack Overflow
Top answer
1 of 4
27

There is no real reason that lambdas can't have default arguments. However, there are two main ways to use lambdas, and only one of them would allow default arguments without changing the type system.

  1. You can call the lambda directly, or through a template. Default parameters would work fine in this case.

  2. You can call the lambda through std::function. Default parameters would not work without changing the type system.

My guess is that new functions that people write in C++11 will usually take std::function parameters because this way, the function won't have to be a template, or it won't have to be instantiated for every single lambda and functor that gets passed to it.

Why can't a std::function (or function pointer) take defaults?

It's not obvious what the type of such a function would be.

  • If it's std::function<int(bool)>, then how do you call it with the defaults? (You can't.)

  • If it's std::function<int(bool=false)>, then what types is it compatible with, and how does conversion work? Can you convert it to std::function<int()>? What about std::function<int(bool=true)>?

  • If it's something new like std::function<int(bool=default)>, then what types is it compatible with, and how does conversion work?

Basically, this isn't just a switch you can flip in the standard and make function pointers / std::function handle default arguments. Default arguments in normal functions are handled using information from the function's declaration, which is not available at the call site for a lambda or function pointer. So you would have to encode information about the defaults into the function type, and then work out all of the non-obvious rules for conversion and compatibility.

So you would have to come up with a compelling case for why such a feature would be added, and convince the committee.

So, why can't lambdas take defaults?

I haven't answered this question. But I don't think it would be a very useful feature to add. I would delete this answer if I could, but it's been accepted. I would downvote it if I could, but it's mine. C'est la vie.

2 of 4
5

I agree that there is no real "technical" restriction per se to allowing default arguments in lambdas to work in some cases. It wouldn't mangle your pointers and auto, because the type of a function is not affected by default arguments. But that's also why this wouldn't be terribly practical.

Why?

Because default arguments, while part of a function signature, are not part of a function type:

[C++11: 1.3.17]:
signature
<function> name, parameter type list (8.3.5), and enclosing namespace (if any)
[ Note: Signatures are used as a basis for name mangling and linking. —end note ]

[C++11: 8.3.5/6]: [..] The return type, the parameter-type-list, the ref-qualifier, and the cv-qualifier-seq, but not the default arguments (8.3.6) or the exception specification (15.4), are part of the function type. [ Note: Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions. —end note ]

They are essentially a piece of syntactic sugar that are "activated" by the compiler being able to see the declaration of the function you use, and injected at the point of the function call:

#include <iostream>

void foo(int x = 5)
{
   std::cout << x << '\n';
}

int main()
{
   foo();
}
  • Output: 5

The default argument is "visible".

However, when you "hide" your function behind a pointer:

int main()
{
    void (*bar)(int) = &foo;
    bar();
}
  • Error: too few arguments to function

The type of bar is correct, and the compiler knows that foo has a default, but there's simply no direct syntax that exists to inform the compiler at the point of calling bar that bar is also foo. Sure, in this trivial scenario it could figure it out by observing the assignment, but that's hardly justification for the wider argument.

For the same reason, default arguments stated only in a definition not visible at the call site are next to useless:

// a.h
void foo(int);

// a.cpp
#include "a.h"
#include <iostream>

void foo(int x = 5)
{
   std::cout << x << '\n';
}

// main.cpp
#include "a.h"

int main()
{
    foo();
}
  • Error in main.cpp: too few arguments to function

I imagine that this is the reason for:

[C++11: 8.3.6/4]: [..] Declarations in different scopes have completely distinct sets of default arguments. [..]

We are allowed to "pile up" default arguments for class non-template member function definitions ([C++11 8.3.6/6]); the example indicates that this default will still only apply in the same TU, which follows the behaviour we've seen above in my second code snippet.

So, if default arguments are not part of the function type, and must be unambiguously visible to the call site, then there are only a handful of trivially contrived corner-cases in which they would be useful for lambdas, which is when they are called in the same scope that they are created so the compiler can tractibly figure out how to "fill in" the default argument for the call to the lambda and, well, what is the point of that? Not a lot, I tell thee.

Discussions

Is default argument value the capture syntax in lambda
The other languages like C++, Swift, Rust use special syntax for capture. I just test the following snippet. And find out if the default argument value is a variable for lambda, it is a capture. I think this is pretty simple and smart. Please correct me if I was wrong. More on discuss.python.org
🌐 discuss.python.org
4
0
March 11, 2022
[C++20] Is a lambda expression a legal default (non-type template) argument, and, if so, would this imply that each instantiation using such a default argument instantiates a unique specialization?
I can't give you a straight answer. I would assume that when present as a default template type it must be the same across all specialisations. It makes no sense to allow it to be a unique type each instantiation. I think some adjustments to the wording in the standard/proposals is needed. Otherwise, it's going to result in witchcraft. More on reddit.com
🌐 r/cpp_questions
9
21
November 9, 2020
c++ - Why does a default argument for a lambda argument trigger a -pedantic" GCC warning? - Stack Overflow
For example, what's the fundamental problem with the original lambda: auto lambda = [](bool a, bool b = true) { return !a && b;};? You can write a closure by hand that will work just fine. 2011-06-08T20:34:07.083Z+00:00 ... The place where I frequently want default arguments to lambdas is when ... More on stackoverflow.com
🌐 stackoverflow.com
[Proposal]: Support default parameter values in lambdas (VS 17.5, .NET 8)
Support default parameter values in lambdas Proposed Prototype: No prototype needed Implementation: done Specification: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/lambda-m... More on github.com
🌐 github.com
53
April 22, 2022
🌐
Medium
medium.com › @python-javascript-php-html-css › recognizing-lambda-behavior-in-c-default-arguments-cf4387975804
Recognizing Lambda Behavior in C++ Default Arguments
September 30, 2024 - The function foo is defined in the first script, and its default argument is a lambda. The presence of a static int x = 0 in this lambda guarantees that the value of the variable x is maintained in between calls.
🌐
Pythondoeswhat
pythondoeswhat.com › 2012 › 11 › lambda-default-parameters.html
Python Does What?!?: lambda default parameters
November 12, 2012 - Kind of like "hey guys, check it out you can just duct tape down the dead-man's switch on this power tool and use it one handed". In Python. Making lambdas behave nicely by using default parameters >>> [c() for c in [(lambda: i) for i in range(10)]] [9, 9, 9, 9, 9, 9, 9, 9, 9, 9] >>> [c() for ...
🌐
Google Groups
groups.google.com › a › isocpp.org › g › std-discussion › c › S1kmj0wF5-g
May a lambda expression be a default template argument?
Lambdas cannot appear in unevaluated contexts, that's why it doesn't compile, even if you put decltype around the lambda: template <class T, class U = decltype([](const T&){return true;})> void f(U u = U()); Also, the "=U()" part won't work as well, not even in C++14. I had the proposal for default constructor of closures a while back, but didn't pursue it because of this unevaluated context part. What is a shame, the new generic captures and polymorphic arguments make lambdas an insanely powerful tool to define functors, would be awesome if we could use them as template arguments.
🌐
Cppreference
en.cppreference.com › w › cpp › language › default_arguments.html
Default arguments - cppreference.com
February 18, 2025 - The ellipsis is not a parameter, and so can follow a parameter with a default argument: ... Default arguments are only allowed in the parameter lists of function declarations and lambda-expressions,(since C++11) and are not allowed in the declarations of pointers to functions, references to ...
Find elsewhere
🌐
Reddit
reddit.com › r/cpp_questions › [c++20] is a lambda expression a legal default (non-type template) argument, and, if so, would this imply that each instantiation using such a default argument instantiates a unique specialization?
r/cpp_questions on Reddit: [C++20] Is a lambda expression a legal default (non-type template) argument, and, if so, would this imply that each instantiation using such a default argument instantiates a unique specialization?
November 9, 2020 -

(I previously posted this question including a bounty at SO, but without getting an answer; trying my luck here at r/cpp_questions)

Basic premise / preconditions

The basic premise for this question is that captureless lambdas are structural types (in C++20), such that they can be used as arguments for (auto) non-type template parameters:

// Premise: this snippet is well-formed in C++20.
template<auto v>
constexpr auto identity_v = v;

constexpr auto l1 = [](){};
constexpr auto l2 = identity_v<l1>;

which I made an attempt at answering in the following SO Q&A, but where some comments from Davis Herring pointed out a potential hole w.r.t. implementation-defined (unspecified) members of the associated closure type that could, in lack of explicit disallowance, be non-structural types. Anyway, afaik, for all purposes, the intent in C++20 are for captureless lamdas to be able to be used as non-type template parameters, which leads us to the gist of this question.

Question

  • Is a lambda expression a legal default (non-type) template argument, and, if so, would this imply that each instantiation using such a default argument instantiates a unique specialization?

According to [expr.prim.lambda.closure]/1 the type of each lambda-expression is unique

[...] a unique, unnamed non-union class type, called the closure type [...]

and, moreover, [basic.def.odr]/13.10 states [extract]:

[basic.def.odr]/13 There can be more than one definition of a

  • [...]

  • default template argument,

  • [...]

[...]

(13.10) In each such definition, except within the default arguments and default template arguments of D, corresponding lambda-expressions shall have the same closure type (see below).

The non-normative example [basic.def.odr]/16 mentions

[...] If the definition of g appears in multiple translation units, the program is ill-formed (no diagnostic required) because each such definition uses a default argument that refers to a distinct lambda-expression closure type. [..]

but this makes no mentions of default template arguments, which is something different entirely from default (function) arguments.

On the other hand, [basic.def.odr]/1 [extract, emphasis mine] does explicitly mention definitions of default template arguments:

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, template, default argument for a parameter (for a function in a given scope), or default template argument.

arguably meaning that default template arguments are considered definitions that need to respect the ODR (with some exceptions for the unique close type of lambda-expresson).

Why?

If this is in fact legal, each invocation of say a variable template with a lambda as default argument would result in an instantiation of a unique specialization:

template<auto l = [](){}>
               // ^^^^^^ - lambda-expression as default argument
constexpr auto default_lambda = l;

static_assert(!std::is_same_v<
    decltype(default_lambda<>),
    decltype(default_lambda<>)>);

GCC (HEAD, 11.0.0) accepts the program above without any errors, and Clang rejects it only based on a diagnostic that is not yet supported:

error: sorry, non-type template argument of type '(lambda at prog.cc:14:19)' is not yet supported

If GCC is in fact correct above, this could arguably allow another mechanism to capture and retrieve a meta-programming state, a technique that has since long been deemed, as per CWG open issue 2118, as

... arcane and should be made ill-formed.

🌐
Cppreference
en.cppreference.com › w › cpp › language › lambda.html
Lambda expressions (since C++11) - cppreference.com
It appears within a default member initializer, and its innermost enclosing scope is the corresponding class scope. For such lambda expression, the reaching scope is defined as the set of enclosing scopes up to and including the innermost enclosing function (and its parameters).
🌐
GitHub
github.com › dotnet › csharplang › issues › 6051
[Proposal]: Support default parameter values in lambdas (VS 17.5, .NET 8) · Issue #6051 · dotnet/csharplang
April 22, 2022 - Support default parameter values in lambdas Proposed Prototype: No prototype needed Implementation: done Specification: https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/lambda-method-group-defaults.md Design Discussio...
Author   captainsafia
🌐
Temp Mail
tempmail.us.com › temp mail › blog › c++ › recognizing lambda behavior in c++ default arguments
Recognizing Lambda Behavior in C++ Default Arguments
July 24, 2024 - The function foo is defined in the first script, with a lambda as its default argument. The use of a static int x = 0 in this lambda ensures that the value of the variable x is kept between calls.
🌐
Medium
henriquesd.medium.com › net-8-and-c-12-default-lambda-parameters-746cedf20f45
.NET 8 and C# 12 — Default Lambda Parameters | by Henrique Siebert Domareski | Medium
November 27, 2023 - Default Lambda Parameters it’s a new C# 12 feature that allows you to define default values for parameters on lambda expressions, making it possible now to execute lambda expressions without needing to always inform a parameter value.
🌐
DataFlair
data-flair.training › blogs › python-lambda-expression
Python Lambda Expression - Declaring Lambda Expression & Its Defaults - DataFlair
March 8, 2021 - So, let’s see what we can or can’t do with a lambda. One or more variables appearing in the expression may be declared previously. But if it’s there in the arguments, either it should have a default value or must be passed as an argument to the call.
Top answer
1 of 2
12

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.

2 of 2
7

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.

🌐
Microsoft Community
techcommunity.microsoft.com › microsoft community hub › communities › products › microsoft 365 › excel
Default values for LAMBDA parameters? | Microsoft Community Hub
March 19, 2021 - Is it possible to somehow include default values for LAMBDA functions with multiple parameters? For example, if my function is ... I would like to be able have "height" default to, say, "2" so I can call it with "4" and have it return "8".