需要使用shared_ptr?

Need to use shared_ptr?

我有两个类:Lattice 和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 studio17\community\vc\tools\msvc.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 studio17\community\vc\tools\msvc.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 studio17\community\vc\tools\msvc.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 studio17\community\vc\tools\msvc.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 studio17\community\vc\tools\msvc.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 studio17\community\vc\tools\msvc.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,除非它们是完全必要的。

  • 您正在按值传递 std::unique_ptr,它始终是一个错误的 ,因为它没有复制构造函数。
  • 为什么您的代码甚至无法编译?似乎将 std::unique_ptr 传递给绑定函数是 long-living bug of visual studio.

你需要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. We can make this compile by using a 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 长寿。这真正归结为为什么首先要使用 more than 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 参数。