У меня есть два класса: решетка и ModelGUI. Я хочу передать функцию из Lattice в GUI в качестве обратного вызова. Я реализовал Lattice как unique_ptr. Некоторый код:
ModelGUI.h :
using CheckTypeClbk = std::function<Enums::AgentType(int)>;
ModelGUI(const Matrix* matrix_, CheckTypeClbk callback, float windowHeight_, float windowWidth_, float latticeWidth_);
Main.cpp:
std::unique_ptr<ILattice> lattice(new Lattice(5, qMap));
ModelGUI gui(lattice->getLattice(), std::bind(&ILattice::checkAgentType, lattice, std::placeholders::_1),
800, 1200, 800);
С этой реализацией я получил странные ошибки компиляции о шаблонах:
1>main.cpp
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(390): error C2664: 'std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>::tuple(std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Ph<1>> &&)': cannot convert argument 1 from 'std::unique_ptr<ILattice,std::default_delete<_Ty>>' to 'std::allocator_arg_t'
1> with
1> [
1> _Ty=ILattice
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(389): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1903): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: while compiling class template member function 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)'
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1929): note: see reference to function template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\predator-prey\predator-prey\main.cpp(16): note: see reference to class template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>' being compiled
1> with
1> [
1> _Ty=ILattice
1> ]
Но когда я использую shared_ptr вместо unique_ptr, все работает нормально. Это хорошая практика? Я слышал, чтобы избежать shared_ptr, насколько я могу, если они не являются абсолютно необходимыми.
2 ответа
Вам нужен 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
параметра.
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который будет скомпилирован с помощью компилятора C ++). Используйте тег, зависящий от версии, для вопросов, связанных с конкретной редакцией стандарта [C ++ 11], [C ++ 14], [C ++ 17] или [C ++ 20] и т. Д.