[Development] RFC: lambda or lambda return from lambda?

Jędrzej Nowacki jedrzej.nowacki at theqtcompany.com
Mon Feb 1 10:18:25 CET 2016


On Monday 01 of February 2016 11:08:54 Marc Mutz wrote:
> Hi,
> 
> We're seeing increasing use of lambdas in dev, and I'm a bit unhappy about
> the result.
> 
> E.g. (not picking on Anton here, I have done the same before):
> 
>  auto firstEqualsName = [&name](const QPair<QByteArray, QByteArray> &header)
> {
>       return qstricmp(name.constData(), header.first) == 0;
>  };
>  fields.erase(std::remove_if(fields.begin(), fields.end(),
>                              firstEqualsName),
>               fields.end());
> 
> This is one way to write a unary predicate. But it hides the fact that the
> predicate depends on the parameter 'name' (an argument to the function this
> lambda is defined in).
> 
> With classical function objects, one would have written - at the call site:
> 
>  fields.erase(std::remove_if(fields.begin(), fields.end(),
>                              FirstEquals(name)),
>               fields.end());
> 
> See the difference?
> 
> Now, we don't want to go back and write function objects for one-time use,
> but it would be nice to have this explicit syntax, would it not?
> 
> One way to have this with lambdas is to write a lambda that returns a
> lambda. We can do this, because auto return type deduction works for
> lambdas already in C++11, unlike for normal functions. With this, the above
> would become (hold tight):
> 
>   auto firstEquals = [](const auto &name) {
>     return [&name](auto header) { return qstricmp(header.first, name) == 0;
> }; }
> 
> where I used auto and dropped the const-& argument passing only for
> exposition, to get it onto a single line.
> 
>  fields.erase(std::remove_if(fields.begin(), fields.end(),
>                              firstEquals(name)),
>               fields.end());
> 
> So, where to put the ugliness: on the definition of the lambda, or the call
> site? (Note that "if you use this lambda more than once, then X" is not a
> good answer, because you shouldn't use lambdas more than once. But with the
> lambda- returning-lambda, you could actually reuse them:
> 
> // at global scope:
> 
>   auto firstEquals = [](const auto &name) {
>     return [&name](auto header) { return qstricmp(header.first, name) == 0;
> }; }
> 
>   // use in several functions - always the same type
> 
> Whereas without the outer lambda, each use would create a new type, and
> potentially duplicate code (same problem as QStringLiteral, and the same
> soultion, really: wrap them in a function, except for lambdas, because of
> the unnameable return type, we need to use another lambda instead of a
> classical function).
> 
> So, which one to use?
> 
> Thanks,
> Marc

Hi,

 I would just inline the lambda inside remove_if. That way "name" would be 
explicit in place in which it is used and you could avoid 2nd lambda. 

So it would look like that:

  fields.erase(std::remove_if(fields.begin(), 
                              fields.end(),
                              [&name](const QPair<QByteArray, QByteArray> 
&header)
                                  {
                                      return qstricmp(name.constData(), 
header.first) == 0;
                                  }),
               fields.end());

// I hope that formating is still ok, and the code is not wrapped.

For a bigger code we would actually require named functions. What do you 
think?

Cheers,
 Jędrek



More information about the Development mailing list