Hello everyone,
what would you think about lambda closure types with empty capture lists having a default constructor?
Why do I think it would be beneficial? I want to something similar to:
struct Rect {
double a;
double b;
};
typedef std::set<Rect, decltype([](const Rect& l, const Rect& r){ return l.a*l.b < r.a*r.b; })> RectsByArea;
typedef std::set<Rect, decltype([](const Rect& l, const Rect& r){ return l.a+l.b < r.a+r.b; })> RectsByCirc;
typedef std::set<Rect, decltype([](const Rect& l, const Rect& r){ return l.a*l.a+l.b*l.b < r.a*l.a+r.b*r.b; })> RectsByDiag;
RectsByArea ra;
RectsByCirc rc;
RectsByDiag rd;
Using C++/11 this will fail with the lambda not having a default constructor. Why? The default constructor of std::set is actually a two-parameter constructor with default parameters: first to the comparator, the second to the allocator. Now the allocator here is not the problem, but the comparator, what is here the lambda - and it does not have a default constructor.
For some compilers, there is actually a more-less usable workaround that almost does what I wanted (tested with Visual Studio 2010 and GCC 4.6):
auto byArea = [](const Rect& l, const Rect& r){ return l.a*l.a+l.b*l.b < r.a*l.a+r.b*r.b; }
std::set<Rect, decltype(byArea)> rectsByArea(byArea);
The code here is still (somewhat) local, but for some functions it explodes: for example, the copy assignment operator - being a template, we are good until we want to use them... Also, it breaks under Visual Studio 2012 (and possibly more compilers as well): there, if the comparator has a sizeof of 0, it does not store it, just default-constructs when needed - with the lambda, we are back to square one.
I know this can be avoided by having a few plain old functors with operator(), but the whole idea of lambdas is that the functor is written in-line.
The change to the standard would be "minor": in section 5.1.2.19, it currently states (at least in the latest public draft n3337):
The closure type associated with a lambda-expression has a deleted (8.4.3) default constructor and a deleted
copy assignment operator.
This should be changed to define a default constructor for closures without lambda captures, the same way it defines a conversion operator to a function pointer for them - something similar to:
The closure type for a lambda-expression with no lambda-capture has a public implicitly-declared (12.8) default constructor. Closure types associated with a lambda-expression with more than zero lambda-captures have a deleted (8.4.3) default constructor and a deleted copy assignment operator.
My only concern if default construction could break something - but I could not find anything like that.
What do you think?