C++ Logo

std-discussion

Advanced search

Re: Possible defect: unneeded const in lambda capture

From: Roman Odaisky <roma_at_[hidden]>
Date: Thu, 29 Aug 2019 21:35:50 +0300
Hi,

Thanks for the quick reply.

> See
> https://groups.google.com/a/isocpp.org/d/msg/std-discussion/FAb7nRfmU3I/U7dc
> ZT-MCQAJ

Do I understand correctly that this mailing list is now the right place to
continue that discussion?

I see that last time the following concerns have been raised:

> this change would make the type of [a captured variable] depend on whether
> it's being odr-used or not (note that non-odr-uses of a refer to the
> enclosing entity, not the captured value), which seems unfortunate

But that's already the case. Consider:

    int x = 0;
    int& r = x;
    [r]() {
        decltype(auto) r1{r};
        decltype(r) r2{r}; // ill-formed
    }();

In this lambda an odr-used r is a const lvalue, while a non-odr-used r is of
non-const type.

The odr-use distinction is an issue capable of biting one in the backside even
when the const keyword is completely absent from the code. Perhaps there's an
alternative wording that achieves the same goals using the constexpr machinery
instead of odr-use, but I do not have such a proposal.

To summarize, the proposal only causes visible type discrepancies if 1) the
user is doing meta-programming on both types of captured variables and
expressions involving them 2) in a mutable lambda 3) where constness matters.
I would be surprized to learn this happens at all.

> Such plausible cases have been explained before when this matter has come
> up. Transformation from a loop to an algorithm that uses a lambda was the
> original motivation.

Certainly the user would use a [&] lambda for that? And if the nature of the
algorithm is such that the lambda is stored somewhere and can outlive its
calling scope, necessitating storage of captured values, that's precisely the
use case I started with, which takes advantage of non-const members because
they can be moved.

> Current rule allows
> const int length = 42;
> [=]() mutable { int array[length]; };
> The proposed rule will make that either invalid or extra tricky.

This has been refuted by other participants in the previous discussion because
this isn't an odr-use.

I conclude that the proposed change has tangible benefits and negligible
drawbacks.

However, I do understand how important it is to minimize risks of breaking
existing code. A possibility is to both allow use of move constructors and
have the static types of the captured members be const. For example, the
wording of expr.prim.lambda.capture/11:

    Every id-expression within the compound-statement of a lambda-expression
    that is an odr-use (6.2) of an entity captured by copy is transformed into
    an access to the corresponding unnamed data member of the closure type.

can be updated to go on to say something like

    If that entity had const type, this access happens as though *this had
    const type. /* Doing this for volatile sounds completely unnecessary. */

Or whatever standardese best describes the idea of the members not being const
but access to them being const, in the same way const member functions see
non-const members as const.

What would be your stance on such a conservative revision of the proposal,
where the members aren't const for purposes of move constructors but const
for... to be honest, I don't really see what for, other than possible backward
compatibility?

-- 
WBR
Roman.

Received on 2019-08-29 13:37:58