需要使用shared_ptr吗?

时间:2019-05-30 21:29:04

标签: c++ callback smart-pointers

我有两个类: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,除非它们是完全必要的。

2 个答案:

答案 0 :(得分:2)

您需要shared_ptr吗?

不。至少对于给定的示例而言不是。

如果latticegui是在不同的范围内定义的,并且具有不同的寿命,并且在各处使用,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)

  • 您正在按值传递std::unique_ptr,并且它总是不好的idea,因为它没有副本构造函数。
  • 为什么您的代码甚至无法编译?似乎将std :: unique_ptr传递给绑定函数是Visual Studio的长期bug