【发布时间】: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<T>::expired() 调度一个lambda 以清除weak_ptrs 的连接列表。以前我很高兴,因为使用 clang-6.0+ 或 g++-7+ 编译时一切正常。然后,我不得不使用 MSVC 并在调用 destruct 时收到读取访问冲突。我很震惊,并试图生成一个展示相同问题的最小示例。上面给出了最小的例子。
最小的示例使错误消息清晰,似乎 MSVC lambda 尝试访问 this->__this->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++