【问题标题】:What is a generalized lambda capture and why was it created?什么是广义 lambda 捕获,为什么要创建它?
【发布时间】:2017-01-07 08:30:21
【问题描述】:

我看到很多关于广义 lambda 捕获用于各种事物的问题,但没有任何东西能准确解释它们是什么或为什么将它们添加到标准中。

我已经阅读了似乎是document describing the updated to the standard necessary for generalized lambda captures to exist 的内容,但它并没有真正说明它们的创建原因,也没有真正很好地总结它们的工作原理。主要是一堆干巴巴的“在此处删除并在此处添加该语言”的东西。

那么,它们是什么?我为什么要使用一个?他们遵循什么规则?例如,它似乎允许捕获表达式。何时评估这些表达式?如果它们导致副作用,这些副作用何时生效?如果评估多个表达式,是否有保证的评估顺序?

【问题讨论】:

  • 据我了解(我可能是错的)广义 lambda 捕获严格是为了能够将右值引用传递给 lambda,同时保留其右值引用属性(右值引用类型),但不确定是否它涵盖了 C++11 中尚未提供的更多内容
  • @Peter - 是的,或多或少。它们是复杂的野兽,但我使用过它们,并且基本了解它们的工作原理。以及我使用 lambda 的示例:bitbucket.org/omnifarious/sparkles/src/…
  • 希望结束这个问题的人请在评论中建议如何改进它?
  • 对于几乎所有关于新功能的纯文字论文,都有一篇(通常是较早的)包含设计描述的论文。在这种情况下,它是N3610

标签: c++ lambda c++14


【解决方案1】:

像往常一样,Scott Meyers 在Effective Modern C++ 第 32 条:使用初始化捕获将对象移动到闭包中对它们是什么、为什么要使用它们以及它们遵循的大致规则进行了很好的解释。这是一个简短的摘要:

由于 C++11 仅具有按值和按引用捕获,因此缺少按移动捕获。 C++14 没有添加它,而是引入了 generalized lambda capture,也称为 init capture。它允许您指定

  1. 从 lambda 生成的闭包类中数据成员的名称,以及
  2. 初始化该数据成员的表达式。

有了这个,init 捕获是一种新的更通用的捕获机制,它涵盖了以上三个 by-* 捕获等等(但没有默认捕获模式)。

回到by-move捕获的主要动机,它可以通过一个init捕获来实现,如下所示:

auto pw = std::make_unique<Widget>(); 

// configure *pw

auto func = [pWidget = std::move(pw)] { 
    return pWidget->isValidated() && pWidget->isArchived(); 
};

您可以使用

在 C++11 中模拟初始化捕获
  • 手写类:

    class IsValAndArch {
    public:
        using DataType = std::unique_ptr<Widget>;
    
        explicit IsValAndArch(DataType&& ptr) : pw(std::move(ptr)) {}  
        bool operator()() const { return pw->isValid() && pw->isArchived(); }    
    
    private:
        DataType pw;
    };
    
    auto pw = std::make_unique<Widget>();
    
    // configure *pw;
    
    auto func = IsValAndArch(pw);
    
  • std::bind

    auto pw = std::make_unique<Widget>();
    
    // configure *pw;
    
    auto func = std::bind( [](const std::unique_ptr<Widget>& pWidget)
        { return pWidget->isValidated() && pWidget->isArchived(); },
        std::move(pw) );
    

    请注意,lambda 的参数是一个左值引用(因为在绑定对象中移动的pw 是一个左值)并且有一个 const 限定符(以模拟 lambda 的 const 性;所以如果原始 lambda 被声明为可变的,不会使用 const 限定符)。

Anthony Calandra's cheatsheet of modern C++ language and library features 中还提供了有关初始化捕获的一些详细信息,例如,在创建 lambda 时(而不是在调用它时)评估初始化表达式。

【讨论】:

  • 什么是pw - isValidated()?你在这里打电话给什么样的operator-?你没有在 lambda 中使用初始化的 pWidget,那有什么意义呢?
  • 谢谢,沃尔特。我更正了代码(现在应该定义行为;)
  • 这正是我所希望的答案。谢谢!
  • @Omnifarious 这是另一个新问题。发布它。
  • @Omnifarious:我不知道,但也假设初始化捕获中表达式的求值顺序未定义。但是,从 C++14 开始,各个表达式可能不再相互交错。
猜你喜欢
  • 2011-01-05
  • 2019-05-01
  • 1970-01-01
  • 2010-09-12
  • 2018-09-12
  • 2010-12-08
  • 2011-10-31
  • 2011-12-01
  • 1970-01-01
相关资源
最近更新 更多