Рассмотрим операцию со стандартным асинхронным интерфейсом:Динамически-Выделено станд Реализация класса :: асинхронной-ки его член
std::future<void> op();
Внутренне, op
должно выполнить (переменное) количество асинхронных операций для завершения; число этих операций является конечным, но неограниченным и зависит от результатов предыдущих асинхронных операций.
Вот (плохо) попытка:
/* An object of this class will store the shared execution state in the members;
* the asynchronous op is its member. */
class shared
{
private:
// shared state
private:
// Actually does some operation (asynchronously).
void do_op()
{
...
// Might need to launch more ops.
if(...)
launch_next_ops();
}
public:
// Launches next ops
void launch_next_ops()
{
...
std::async(&shared::do_op, this);
}
}
std::future<void> op()
{
shared s;
s.launch_next_ops();
// Return some future of s used for the entire operation.
...
// s destructed - delayed BOOM!
};
Проблема, конечно, является то, что s
выходит из области видимости, поэтому позже методы не будут работать.
Чтобы изменить это, вот изменения:
class shared : public std::enable_shared_from_this<shared>
{
private:
/* The member now takes a shared pointer to itself; hopefully
* this will keep it alive. */
void do_op(std::shared_ptr<shared> p); // [*]
void launch_next_ops()
{
...
std::async(&shared::do_op, this, shared_from_this());
}
}
std::future<void> op()
{
std::shared_ptr<shared> s{new shared{}};
s->launch_next_ops();
...
};
(Asides от странности объекта вызова метода с общим указателем на себя,) проблема с линией, обозначенной [*]
. Компилятор (правильно) предупреждает, что это неиспользуемая переменная.
Конечно, это можно как-то обмануть, но является ли это признаком фундаментальной проблемы? Есть ли вероятность, что компилятор будет оптимизировать аргумент и оставить метод с мертвым объектом? Есть ли лучшая альтернатива всей этой схеме? Я не считаю полученный код наиболее интуитивным.
Спасибо за полезный ответ! Если оставить аргумент неназванным, это то, что я делаю, и рад, что это законно. –