gpt4 book ai didi

c++ - 为什么非可变 lambda 表达式中的字段在捕获常量值或常量引用时使用 "const"?

转载 作者:行者123 更新时间:2023-12-04 11:28:06 26 4
gpt4 key购买 nike

如问题 lambda capture by value mutable doesn't work with const &? 所示, 当捕获类型为 const T& 的值时使用其名称或 [=]在可变 lambda 中,隐藏类中的字段的类型为 const T .可以争论的是,这对于可变 lambda 来说是正确的做法。
但是为什么对非可变 lambda 也这样做呢?在非可变 lambda 表达式中,operator()(...)已声明 const ,因此无论如何它都无法修改捕获的值。
当我们移动 lambda 时,就会发生这种不良后果,例如将它包装在 std::function 中时。 .
请参阅以下两个示例:

#include <cstdio>
#include <functional>

std::function<void()> f1, f2;

struct Test {
Test() {puts("Construct");}
Test(const Test& o) {puts("Copy");}
Test(Test&& o) {puts("Move");}
~Test() {puts("Destruct");}
};

void set_f1(const Test& v) {
f1 = [v] () {}; // field type in lambda object will be "const Test"
}

void set_f2(const Test& v) {
f2 = [v = v] () {}; // field type in lambda object will be "Test"
}

int main() {
Test t;
puts("set_f1:");
set_f1(t);
puts("set_f2:");
set_f2(t);
puts("done");
}
我们得到以下编译器生成的 lambda 类:
class set_f1_lambda {
const Test v;
public:
void operator()() const {}
};

class set_f2_lambda {
Test v;
public:
void operator()() const {}
};
该程序打印以下内容(使用 gcc 或 clang):
Construct
set_f1:
Copy
Copy
Copy
Destruct
Destruct
set_f2:
Copy
Move
Move
Destruct
Destruct
done
Destruct
Destruct
Destruct
v在第一个示例中,值被复制不少于 3 次 set_f1 .
在第二个例子中 set_f2 ,唯一的拷贝是在捕获值时(如预期)。使用两次移动这一事实是 libstdc++ 中的一个实现细节。第一步发生在内部 operator=std::function当按值将仿函数传递给内部函数时(为什么这个函数签名不使用传递引用?)。第二个移动发生在移动构造最终堆分配的仿函数时。
但是,如果字段是 const,则 lambda 仿函数对象的移动构造函数不能为字段使用移动构造函数。 (因为这样的构造函数不能在窃取其内容后“清除”const 变量)。这就是为什么必须将复制构造函数用于此类字段的原因。
所以对我来说,将值捕获为 const 似乎只有负面影响。在非可变 lambda 表达式中。我是否遗漏了一些重要的东西,或者它只是通过这种方式标准化以某种方式使标准更简单?

最佳答案

Did I miss something important, or has it simply been standardised this way to make the standard more simpler somehow?


最初的 lambda 提议,
  • N2550: Lambda Expressions and Closures:Wording for Monomorphic Lambdas (Revision 4)

  • 区分捕获对象的类型和 lambda 闭包类型的相应数据成员的类型:

    /6 The type of the closure object is a class with a unique name, call it F, considered to be defined at the point where thelambda expression occurs.

    Each name N in the effective capture set is looked up in the contextwhere the lambda expression appears to determine its object type;in the case of a reference, the object type is the type to which the reference refers. For each element in the effective capture set, Fhas a private non-static data member as follows:

    • if the element is this, the data member has some unique name, call it t, and is of the type of this ([class.this],9.3.2);
    • if the element is of the form & N, the data member has the name N and type “reference to object type of N”;5.19. CONSTANT EXPRESSIONS 3
    • otherwise, the element is of the form N, the data member has the name N and type “cv-unqualified object type of N”.

    在这个原始措辞中,OP 的示例不会导致 const - 合格数据成员 v .我们可能还注意到,我们承认措辞

    in the case of a reference, the object type is the type to which the reference refers


    存在于 [expr.prim.lambda.capture]/10 中(但直接说明数据成员的类型而不是对象类型)关于 lambdas 的最终措辞的(最新草案):

    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.


    发生的事情是
  • N2927: New wording for C++0x Lambdas (rev. 2)

  • 其中重写了 N2550 的大部分措辞:

    During the meeting of March 2009 in Summit, a large number of issues relating to C++0xLambdas were raised and reviewed by the core working group (CWG). After deciding on a cleardirection for most of these issues, CWG concluded that it was preferable to rewrite the sectionon Lambdas to implement that direction. This paper presents this rewrite.


    特别是,对于这个问题,解决 CWG 问题
  • CWG 756. Dropping cv-qualification on members of closure objects

  • [...] Consider the following example:

    void f() {
    int const N = 10;
    [=]() mutable { N = 30; } // Okay: this->N has type int, not int const.
    N = 20; // Error.
    }

    That is, the N that is a member of the closure object is not const,even though the captured variable is const. This seems strange, ascapturing is basically a means of capturing the local environment in away that avoids lifetime issues. More seriously, the change of typemeans that the results of decltype, overload resolution, and templateargument deduction applied to a captured variable inside a lambdaexpression can be different from those in the scope containing thelambda expression, which could be a subtle source of bugs.


    之后的措辞(从 N2927 开始)变成了我们最终看到的 C++11

    The type of such a data memberis the type of the corresponding captured entity if the entity is not a reference to anobject, or the referenced type otherwise.


    如果我敢推测,CWG 756 的决议还意味着保留 cv 限定符用于引用类型的实体的值捕获,这可能是一种疏忽。

    关于c++ - 为什么非可变 lambda 表达式中的字段在捕获常量值或常量引用时使用 "const"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68734721/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com