C++ Logo

std-discussion

Advanced search

Re: lambda, capture by const-ref

From: Federico Kircheis <federico.kircheis_at_[hidden]>
Date: Fri, 17 Sep 2021 08:23:14 +0200
On 17/09/2021 04.42, Thiago Macieira via Std-Discussion wrote:
> On Wednesday, 15 September 2021 14:05:06 PDT Federico Kircheis via Std-
> Discussion wrote:
>> So, to sum it up, I would like see the possibility to add const to the
>> lambda capture.
>> Just this qualifier (leaving thus volatile out), and not the type (it
>> can be done independently if we acknowledge it might be a good idea).
>
> Hello Federico
>
> Do you have a different use-case for this?

This is the main use-case I have, but generally being able to declare a
variable const is a nice feature, and using std::as_const is both more
verbose, and does not always work (while the macro, AFAIK, does).

> Because you had a working, ideal
> solution, then you created a problem and you're now looking for a solution to
> that problem you created.


What was the working, ideal solution?
Calling unknown code while locking a mutex is definitely not ideal, and
might be broken.
But unfortunately it is the most simple variation of code to write.
I think its crucial to have an "easy"/simple way to write the correct code.

> What's wrong with:
>
> ----
> bool foo(data_and_mutex& d_m){
> const auto& value = get_value();
> return d_m.lock([&](data& d){
> bool res = d.str == value;
> if(res){d.i++;}
> return res;
> } );
> }
> ----
>
> You wrote:
>> In this case, `foo` does not do much work, but we expanded a lot the
>> scope of the variable returned by get_value.

Exactly.
Moving the call (from existing code) one layer up might cause name
clashes, and introduces the possibility to use the wrong variable.
All disadvantages that exist when variables are not declared in the
innermost scope.

> Not in this case.

Because this is a trimmed down example.

> And it's just a matter of adding more braces if you have
> further code and would like to terminate the lifetime extension as early as
> possible.
>


Yes, adding braces works in some cases.


----
bool foo(data_and_mutex& d_m){
    {
        const auto& value = get_value();
        return d_m.lock([&](data& d){
            bool res = d.str == value;
            if(res){d.i++;}
            return res;
        } );
    }
  }
----
but what if we return something from the lambda?
Without the braces, the code would be:
----
bool foo(data_and_mutex& d_m){
        const auto& value = get_value();
        /*const*/ auto result = d_m.lock([&](data& d){
            bool res = d.str == value;
            if(res){d.i++;}
            return res;
        } );
        // do something with result
  }
----
If val needs to outlive the scope of the braces you are proposing to add 
(very probably as those braces are there solely for limiting the scope 
of another variable), you are ultimately proposing to use a two-step 
initialisation, which might not always be possible.
For example if the returned type does not have a default-constructor (or 
an expensive constructor).
Another limitation is that it denies the possibility to store it in a 
const variable.
But issues can be avoided with another layer of indirection; a second 
lambda:
----
bool foo(data_and_mutex& d_m){
     auto result = [](){
        const auto& value = get_value();
        return d_m.lock([&](data& d){
            bool res = d.str == value;
            if(res){d.i++;}
            return something_without_default_or_expensive_constructor(res);
        } );
     }();
     // do something with result
  }
----
which is doable, but ... it is a lot of work.
And in practice, people will write the first snippet I've presented 
which is problematic, but much simpler to write and understand
We added the possibility to declare variables inside if-statements, like
if (int a = f(), a != 0)
{
   // ...
}
this could also be achieved with a couple of braces
{
   int a = f();
   if ( a != 0)
   {
     // ...
   }
}
but turns out, that in practice, those couple of braces are "forgotten" 
most of the time/are easy to oversee.
For the use-case I've presented (and possibly other when one wants, but 
not necessarily needs, to capture by const-ref), I believe the proposed 
change would have the same benefit.

Received on 2021-09-17 01:26:59