【问题标题】:c++: MSVC vs. GCC+CLANG: Handling lambdas capturing class member variables, what is the correct approach?c++:MSVC 与 GCC+CLANG:处理 lambdas 捕获类成员变量,正确的方法是什么?
【发布时间】:2018-10-09 05:15:21
【问题描述】:

考虑以下看起来很无辜的代码段:

#include <functional>
#include <iostream>
#include <list>
#include <memory>

struct foo;

std::list<std::weak_ptr<foo>> wptrs;
std::function<void()> global_func;

struct foo {
    int &a;
    foo(int &a) : a{ a } {  }
    ~foo() {
        global_func = [&] {
            wptrs.remove_if([](auto &x) { return x.expired(); });
            std::cout << "a= " << a << std::endl;
        };
    }
};

int main() {
    int a = 5;
    auto ptr = std::make_shared<foo>(a);
    wptrs.emplace_back(ptr);
    ptr = nullptr;  // object is destroyed here
    global_func();
    return 0;
}

当我第一次在 MSVC (Visual Studio 2017) 上遇到问题时,我正在使用 TCP/IP 服务器,它试图将 weak_ptrs 的列表清理到连接对象。连接对象通过调用weak_ptr&lt;T&gt;::expired() 调度一个lambda 以清除weak_ptrs 的连接列表。以前我很高兴,因为使用 clang-6.0+ 或 g++-7+ 编译时一切正常。然后,我不得不使用 MSVC 并在调用 destruct 时收到读取访问冲突。我很震惊,并试图生成一个展示相同问题的最小示例。上面给出了最小的例子。

最小的示例使错误消息清晰,似乎 MSVC lambda 尝试访问 this-&gt;__this-&gt;a。这个访问顺序表明 MSVC 不捕获a 的地址(或对a 的引用),而是捕获*this 的地址并使用此对象获取对a 的访问。由于*this 对象在弱引用计数变为零时被完全释放,所以我遇到了内存访问错误。

显然,MSVC 方法与 g++ 和 clang 的方法完全不同。那么,我的问题是哪个编译器是正确的?

附: MSVC 案例的简单修复:

#include <functional>
#include <iostream>
#include <list>
#include <memory>

struct foo;

std::list<std::weak_ptr<foo>> wptrs;
std::function<void()> global_func;

struct foo {
    int &a;
    foo(int &a) : a{ a } {  }
    ~foo() {
        global_func = [a = &a] {    // capture a ptr instead
            wptrs.remove_if([](auto &x) { return x.expired(); });
            std::cout << "a= " << *a << std::endl;
        };
    }
};

int main() {
    int a = 5;
    auto ptr = std::make_shared<foo>(a);
    wptrs.emplace_back(ptr);
    ptr = nullptr;  // object is destroyed here
    global_func();
    return 0;
}

【问题讨论】:

    标签: c++ visual-c++ visual-studio-2017 g++ clang++


    【解决方案1】:

    *this 的成员从不被捕获:他们 can’t be explicitly captured,并使用它们 implicitly captures *this(通过引用而不考虑 capture-default;考虑使用 @ 987654325@ 为清楚起见)。所以你的第二个例子是唯一正确的方法; GCC 和 Clang 可能一直在优化 foo::a 的使用,因为引用不能被反弹。

    【讨论】:

      猜你喜欢
      • 2014-05-11
      • 1970-01-01
      • 2019-04-09
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多