C++ Logo

std-discussion

Advanced search

Possible defect: unneeded const in lambda capture

From: Roman Odaisky <roma_at_[hidden]>
Date: Thu, 29 Aug 2019 17:28:54 +0300
Lambda captures in the code

    X const cx;
    X const& cr = cx;
    auto lambda = [cx, cr](){};

are governed by C++17 8.1.5.2.10 and N4830 7.5.5.2.10, which read, in part:

    The type of such a data member is the referenced type if the entity is a
    reference to an object, an lvalue reference to the referenced function
    type if the entity is a reference to a function, or the type of the
    corresponding captured entity otherwise.

This causes the members of the lambda object corresponding to the captures to
be of type X const. This is undesirable for the following reasons:

1. This causes the move constructor of the lambda to call the copy constructor
of X for the const members.
2. This goes contrary to the rules of auto var{expr} (that don’t propagate
constness unless explicitly specified) and introduces a discrepancy where
[x](){} and [x{x}](){} do not mean the same thing.
3. This does not provide any kind of security or const correctness because the
operator() is const anyway unless the lambda is explicitly marked as mutable,
so all by-copy captures are constant expressions regardless of cv-
qualifications of the original variables.

Severity:

Minor. Causes unneeded copies when passing lambdas around, particularly if
it‘s an async I/O callback that stores a bunch of data to be sent at a later
point. Most likely occurrence is when capturing function arguments passed by
const reference, or when a const object is passed by forwarding reference.

Suggested resolution:

rewrite the quoted part of 7.5.5.2.10 to (additions in caps):

    The type of such a data member is the CV-UNQUALIFIED referenced type if
    the entity is a reference to an object, an lvalue reference to the
    referenced function type if the entity is a reference to a function, or
    the CV-UNQUALIFIED type of the corresponding captured entity otherwise.

Impact on existing code:

Most likely unnoticeable, primarily due to item 3 above. Would cause some
members of mutable lambdas to stop being const expressions. Would cause move
constructors to be utilized in place of copy constructors in some cases.

Notes:

Please correct me if there’s a better way in standardese to denote the removal
of top-level cv-qualifiers. As written, the clause also mandates preservation
of volatile, which makes even less sense than preservation of const.

-- 
WBR
Roman.

Received on 2019-08-29 09:31:02