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.
You can call the lambda directly, or through a template. Default parameters would work fine in this case.
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 tostd::function<int()>? What aboutstd::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 OverflowThere 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.
You can call the lambda directly, or through a template. Default parameters would work fine in this case.
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 tostd::function<int()>? What aboutstd::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.
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.
You could use overloading:
int foo(int i)
{
return foo(i, [](int x) -> int { return x / 2; });
}
int foo(int i, std::function<int(int)> f)
{
return f(i);
}
This seems to be a bug in gcc; the standard permits lambda expressions in default parameters as long as nothing is captured.
The following seems to be everything the FDIS says about lambdas in default parameters, so any use other than what is forbidden here ought to be permitted by default.
C++11 FDIS 5.1.2/13
A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity.
[ Example:
void f2() { int i = 1; void g1(int = ([i]{ return i; })()); // ill-formed void g2(int = ([i]{ return 0; })()); // ill-formed void g3(int = ([=]{ return i; })()); // ill-formed void g4(int = ([=]{ return 0; })()); // OK void g5(int = ([]{ return sizeof i; })()); // OK }— end example ]
Is default argument value the capture syntax in lambda
[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?
c++ - Why does a default argument for a lambda argument trigger a -pedantic" GCC warning? - Stack Overflow
[Proposal]: Support default parameter values in lambdas (VS 17.5, .NET 8)
Videos
(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.
Section 5.1.2 paragraph 5 specifically says that you can not have default values for the parameters.
Default arguments (8.3.6) shall not be specified in the parameter-declaration-clause of a lambda-declarator.
Since C++14 it is allowed. It was found to be a defect long time ago: Default arguments for lambdas, and also Default arguments in lambda-expressions.
I want to do:
var myLambda = (int myNum, string myStr = "default string") => {
DoSomething(myNum);
ProcessString(myStr);
};
myLambda(1, "new string");
myLambda(2);Spoiler: You can't do it because optional/default parameters are not allowed in lambdas, but there is a proposal out for it. Which is a damn shame because I love how sleek lambdas are.
What are the alternative ways achieve this?
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.
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.
Lambda's take the same signature as regular functions, and you can give reg a default:
f = lambda X, model, reg=1e3: cost(X, model, reg=reg, sparse=np.random.rand(10,10))
What default you give it depends on what default the cost function has assigned to that same parameter. These defaults are stored on that function in the cost.__defaults__ structure, matching the argument names. It is perhaps easiest to use the inspect.getargspec() function to introspect that info:
from inspect import getargspec
spec = getargspec(cost)
cost_defaults = dict(zip(spec.args[-len(defaults:], spec.defaults))
f = lambda X, model, reg=cost_defaults['reg']: cost(X, model, reg=reg, sparse=np.random.rand(10,10))
Alternatively, you could just pass on any extra keyword argument:
f = lambda X, model, **kw: cost(X, model, sparse=np.random.rand(10,10), **kw)
have you tried something like
f = lambda X, model, **kw: cost(X, model, sparse = np.random.rand(10,10), **kw)
then reg (and any other named argument you want to pass through (other than sparse)) should work fine.