【问题标题】:Accessing variables in lambda without capture list在没有捕获列表的情况下访问 lambda 中的变量
【发布时间】:2017-01-24 13:39:44
【问题描述】:

我想知道为什么我可以在 lamdas 中使用全局变量(感谢 Chris Drew 纠正我)以及为什么我不需要捕获它们:

#include <iostream>
#include <vector>
using namespace std;

size_t i = 0;
vector<int> v = {1,2,3};

int main()
{
    auto lambda = [](){i = v.size();};
    lambda();
    cout << i << endl;

    return EXIT_SUCCESS;
}

在这个最小的工作示例中,我正在访问 size_t 和向量而不捕获它们。如果它们在主方法中声明,我将不得不这样做。为什么会这样,我如何复制 size_t 和向量?我尝试使用[=] 作为捕获列表,但它不会复制vi

【问题讨论】:

标签: c++ list variables lambda capture


【解决方案1】:

按值捕获全局变量的一种方法是使用 C++14 generalized lambda captures

#include <iostream>
#include <vector>

size_t i = 0;
std::vector<int> v = {1,2,3};

int main() {
    auto lambda = [myi = i, myv = v]()mutable{myi = myv.size();};
    lambda();
    std::cout << i << std::endl;
}

Live demo.

【讨论】:

    【解决方案2】:

    您的 lambda 基本上被转换为仿函数,它与:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    size_t i = 0;
    vector<int> v = {1,2,3};
    
    struct lambda
    {
      void operator()() { i = v.size(); }
    };
    
    int main()
    {
        lambda x;
        x();
        cout << i << endl;
    
        return EXIT_SUCCESS;
    }
    

    如您所见,lambda 可以完美地访问任何全局变量,甚至在他们的名字中,该变量是全局可访问的。

    如果 ivmain() 本地的,那么我们就会遇到问题,我们必须捕获它们。

    【讨论】:

    • 谢谢,我的问题的核心,虽然我表达的不是很好,但是问我如何复制vector和size_t。看来我应该用新的 size_t 和向量复制它们。
    • @Bambino 是的,您可以将它们复制到 lambda 中,然后处理这些变量。
    【解决方案3】:

    Lambda 可以访问全局变量和类中的静态变量,而无需显式捕获它们。如果它是一个局部变量,那么你的程序就会是格式错误的。

    #include <iostream>
    #include <vector>
    using namespace std;
    int main()
    {
        size_t i = 0;
        vector<int> v = {1,2,3};
        auto lambda = [](){i = v.size();};   //Error, 
        lambda();
        cout << i << endl;
    
        return EXIT_SUCCESS;
    }
    

    如所见here

    与类中的静态变量相同:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    struct S{
        void touch(){ []{ k = 89; }(); }
        static int getK(){ return k; }
    private:
        static int k;
    };
    
    int S::k = 0;
    
    
    int main()
    {
        S s;
        std::cout << S::getK() << std::endl;
        s.touch();
        std::cout << S::getK() << std::endl;
    }
    

    Here

    【讨论】:

    • 我知道。我只是用了错误的术语来描述向量和 size_t。我想知道如何复制 size_t 和向量?使用[=] 作为捕获列表不起作用。
    • 如果捕获了局部变量,需要修改局部捕获,需要使用mutablelambda
    • 在使用局部变量时使用 mutable 似乎有效。但是将它与全局变量一起使用会产生原始输出。
    【解决方案4】:

    在您的情况下,iv 是全局变量,可供整个 TU 访问。

    当您询问如何按值捕获它们时,我认为您应该能够使用[=] 或列出变量[i, v] 来捕获它们,但这会导致错误,因为它们将是只读的并且您在 lambda 体内分配给 i

    选项 1:
    通过 ref 捕获 i 并按值捕获 v(如果这有意义的话……):

    #include <iostream>  
    #include <vector>  
    using namespace std;
    int main() {
        size_t i = 0;
        vector<int> v = {1,2,3};
        auto lambda = [&i,v](){i = v.size();};
        lambda();
        cout << i << endl;
    
        return EXIT_SUCCESS; }
    

    http://ideone.com/fkn4za

    选项 2:
    使用可变 lambda 并按值捕获两者(这更没有意义)。
    IE。 see this question on SO.
    请注意,在这种情况下,i 也将按值捕获,因此不会分配全局 i,保持值 == 0。

    http://ideone.com/qwlFVv

    【讨论】:

    • 我该如何复制它?使用[=] 作为捕获列表不起作用
    • 查看我对答案的补充。目前还不清楚为什么要按价值(两者)捕获它们。
    • 我期待看到一个零被打印出来,因为我没有对全局变量进行操作,因为我试图复制它们。
    • 好的,我想我已经找到了不可能的原因。显式捕获全局应该是错误或 UB,因为 §5.1.2/10 说:“捕获列表中的标识符是使用非限定名称查找 (3.4.1) 的常用规则查找的;每个这样的查找都应该找到在本地 lambda 表达式的到达范围内声明的具有自动存储持续时间的变量。"
    • 关于SO还有更详细的回答:stackoverflow.com/questions/9199744/…
    猜你喜欢
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 2012-08-11
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    相关资源
    最近更新 更多