Рассмотрим следующий сегмент кода, который выглядит очень невинно:
#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_ptr
s для объектов подключения.Объект соединения планирует лямбду для очистки списка weak_ptr
с соединений, вызывая weak_ptr<T>::expired()
.Я был счастлив раньше, потому что все работало нормально при компиляции с clang-6.0+ или g ++ - 7+.Затем я должен был использовать MSVC и получил нарушение прав на чтение при вызове destruct.Я был шокирован и попытался создать минимальный пример, демонстрирующий ту же проблему.Минимальный пример приведен выше.
Минимальный пример прояснил сообщения об ошибках, и казалось, что MSVC lambda пытается получить доступ к this->__this->a
.Эта последовательность доступа предполагает, что MSVC не захватывает адрес a
(или ссылку на a
), а скорее захватывает адрес *this
и получает доступ к a
, используя этот объект.Поскольку объект *this
полностью освобождается, когда слабый счетчик ссылок становится равным нулю, у меня возникает ошибка доступа к памяти.
Очевидно, что подход MSVC радикально отличается от подхода g ++ и clang.Итак, мой вопрос, какой компилятор подходит?
PS Простое исправление для случая 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;
}