Вызов деструктора std :: function во время его выполнения - PullRequest
0 голосов
/ 12 февраля 2019

Я хочу динамически изменить поведение метода класса, поэтому я реализовал этот метод, вызывая оператор () для std :: function , содержащий копию одногоЛямбда-функция, которая зависит от некоторых значений, известных только после создания класса, одновременно.Лямбды изменяют состояние класса, поэтому они сбрасывают контейнер, содержащий поведение всех динамических методов.
Реализуя приведенную выше идею, я не смог получить доступ к списку захвата лямбы после сброса контейнера.
Следующий фрагмент кода воспроизводит проблему:

std::vector< std::function<void(std::string)> > vector;

int main() {

   //Change class state when variable value will be known
   std::string variableValue = "hello";

   auto function = [variableValue](std::string arg) {
     std::cout <<"From capture list, before: "<< variableValue << std::endl;
     std::cout <<"From arg,          before: " << arg << std::endl;

     vector.clear();

     std::cout << "From capture list, after: " << variableValue << std::endl;
     std::cout << "From arg,          after: " << arg << std::endl;
   };

   vector.push_back(function);

   //Dynamic method execution
   vector[0](variableValue);

   return 0;
}

Создание вывода:

From capture list, before: hello
From arg,          before: hello
From capture list, after:
From arg,          after: hello

, где variableValue недействительно после того, как vector был очищен.

Является ли аннулирование списка захвата ожидаемым результатом?Безопасно ли использовать любую другую локальную переменную, не только в списке захвата, после вызова деструктора std :: function ?Есть ли предлагаемый способ / шаблон для обеспечения того же поведения более безопасным способом (исключая огромные переключатели / если в состояниях класса)?

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Мы можем избавиться от std::function, лямбды и вектора для этого вопроса.Поскольку лямбда-выражения являются просто синтаксическим сахаром для классов с оператором вызова функции, ваш тестовый сценарий фактически такой же, как этот:

struct Foo
{
   std::string variableValue = "hello";

   void bar(std::string arg)
   {
      std::cout <<"From capture list, before: "<< variableValue << std::endl;
      std::cout <<"From arg,          before: " << arg << std::endl;

      delete this;  // ugrh

      std::cout << "From capture list, after: " << variableValue << std::endl;
      std::cout << "From arg,          after: " << arg << std::endl;
   }
};


int main()
{
    Foo* ptr = new Foo();
    ptr->bar(variableValue);
}

Аргумент функции в порядке, потому что это копия, но после delete this членFoo::variableValue больше не существует, поэтому ваша программа имеет неопределенное поведение при попытке его использовать.

Общепринято, что продолжение запуска самой функции допустимо (поскольку определения функций не являютсяобъекты и не могут быть "удалены"; они являются просто фундаментальным свойством вашей программы), если вы оставляете членов инкапсулирующего класса достаточно хорошо в одиночку.

Однако я бы посоветовал избегать этого шаблона, если вы на самом деле ненужно это.Будет легко спутать людей с обязанностями владения вашего класса (даже если «ваш класс» автономно генерируется из лямбда-выражения!).


Является ли список захватааннулирование ожидаемого результата?

Да.

Безопасно ли использовать любую другую локальную переменную, не только в списке захвата, после вызова std :: function destructor?

Да.

Существует ли предлагаемый способ / шаблон для обеспечения того же поведения более безопасным способом (исключая огромные переключатели / если в состояниях класса)?

Невозможно сказать наверняка, не понимая, что вы пытаетесь сделать.Но вы можете попробовать поиграть с сохранением shared_ptr s в вашем векторе ... Просто будьте осторожны, чтобы не захватить shared_ptr в самой лямбде, иначе никогда не будет очищено !Вместо этого может быть полезен захват weak_ptr;оно может быть «преобразовано» в shared_ptr внутри лямбда-тела, которое защитит жизнь лямбды на протяжении всего тела.

0 голосов
/ 12 февраля 2019
Деструктор

std::function уничтожает объект target , если объект не пустой , где target - обернутый вызываемый объект.

В вашем случае целью является лямбда-выражение.Когда вы используете лямбда-выражение, компилятор генерирует «не объединенный неагрегированный тип класса», который содержит захваты по значению в качестве членов данных и имеет operator() в качестве функции-члена .

Когда вы выполняете vector.clear(), запускаются деструкторы его элементов, и поэтому запускаются деструкторы захватов по значению замыкания, которые являются переменными-членами.

Что касается захватов -по ссылке, "время жизни ссылочной переменной заканчивается, когда заканчивается время жизни объекта замыкания."

Таким образом, небезопасно получить доступ к любому захвату, как по значению, так и по ссылке,после запуска деструктора std::function.

А как насчет фактического operator()? «Функции не являются объектами», , поэтому они не имеют времен жизни .Итак, простое выполнение operator() после того, как деструктор был запущен, должно быть в порядке, пока вы не получите доступ к каким-либо захватам.См. условия, при которых можно безопасно delete this.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...