我有两个类:Lattice和ModelGUI。我想将函数从Lattice传递给GUI作为回调。我将莱迪思实现为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,除非它们是完全必要的。
答案 0 :(得分: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
is copied and produced the expected output from the function。
std::unique_ptr
无法复制,因为那样会破坏职位描述的整个 unique 部分。我们看到,如果我们稍微改变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);
}
As expected, this doesn't compile.我们可以使用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;
以上?我们真的需要一个指针吗?
但是现在让我们开始吧,因为在前往绿色牧场之前,至少可以使它看起来更漂亮。这跟lambda表达式是一样的
std::function<void(int)> f1 = [&tp](int i) { tp->func(i); };
通常我不是“ Lambda比bind
更容易阅读”的拥护者,但是这种情况是一个非常有说服力的论点。
回到基础知识上来,实际上并没有什么不同
int main()
{
test t;
std::function<void(int)> f1 = [&t](int i) { t.func(i); };
f1(1);
}
并完全消除了指针。没有指针,没有shared_ptr
。
如果t
容易引起人们的注意,则唯一的用户就是回调函数,则让lambda随身携带t
的副本,并使原始副本死亡。
std::function<void(int)> scopedemo()
{
test t;
return [t](int i) mutable { t.func(i); }; //
}
int main()
{
auto f1 = scopedemo();
f1(1);
}
请注意mutable
。 Lambda默认情况下带有常数,不能用于调用非const
方法或用作非const
参数。
答案 1 :(得分:0)