Вам нужно shared_ptr
?
Нет. По крайней мере, не для данного примера.
Если lattice
и gui
определены в разных областях с разной продолжительностью жизни и используются повсеместно, wowie-zowie, мы можем говорить о shared_ptr
.
Почему?
Давайте начнем с очень простого примера, который показывает, почему unique_ptr
вызывает горе.
#include <functional>
#include <iostream>
struct test
{
test() = default;
test(const test &)
{
std::cout << "copied" << std::endl;
}
void func(int i)
{
std::cout << i << std::endl;
}
};
int main()
{
test t;
std::function<void(int)> f1 = std::bind(&test::func, t, std::placeholders::_1);
f1(1);
}
test
ничего не делает, кроме как сообщает нам, когда объект копируется, и доказывает, что функция запущена. Выполнив его, мы увидим, что t
скопирован и выдает ожидаемый результат от функции .
std::unique_ptr
не может быть скопировано, потому что это в значительной степени разрушило бы всю уникальную часть описания работы. Мы видим, что если мы немного изменим main
, чтобы использовать unique_ptr
, и немного приблизимся к поставленному вопросу.
int main()
{
std::unique_ptr<test> tp = std::make_unique<test>();
std::function<void(int)> f1 = std::bind(&test::func, tp, std::placeholders::_1);
}
Как и ожидалось, это не компилируется. Мы можем сделать эту компиляцию, используя std::reference_wrapper
std::function<void(int)> f1 = std::bind(&test::func, std::reference_wrapper<std::unique_ptr<test>>(tp), std::placeholders::_1);
или предоставить необработанный указатель на bind
std::function<void(int)> f1 = std::bind(&test::func, tp.get(), std::placeholders::_1); f1(1);
, но для этого требуется tp
, чтобы иметь более широкий охват и гарантированно пережить f1
. На самом деле, к чему это приводит, зачем использовать больше, чем test t;
? Нам действительно нужен указатель здесь?
Но давайте пойдем с этим сейчас, потому что мы можем, по крайней мере, сделать этот вид намного красивее, прежде чем отправиться на более зеленые пастбища. Вот то же самое с лямбда-выражением
std::function<void(int)> f1 = [&tp](int i) { tp->func(i); };
Обычно я не сторонник "лямбда легче читать, чем bind
", но этот случай довольно убедительный аргумент.
Возвращаясь к основам, на самом деле это ничем не отличается от
int main()
{
test t;
std::function<void(int)> f1 = [&t](int i) { t.func(i); };
f1(1);
}
и полностью исключает указатель. Нет указателя, нет shared_ptr
.
Если t
может быть запущено и забыто, единственным пользователем является обратный вызов, пусть лямбда возьмет с собой копию t
и пусть оригинал умрет.
std::function<void(int)> scopedemo()
{
test t;
return [t](int i) mutable { t.func(i); }; //
}
int main()
{
auto f1 = scopedemo();
f1(1);
}
Обратите внимание на mutable
. По умолчанию лямбда-код переносит константы и не может использоваться для вызова не const
методов или использоваться в качестве не const
параметра.