C++11 采用共享\u ptr和唯一\u ptr的函数的代码复制

C++11 采用共享\u ptr和唯一\u ptr的函数的代码复制,c++11,parameters,smart-pointers,code-duplication,C++11,Parameters,Smart Pointers,Code Duplication,问题: 让我们假设我有一个算法,它将一个唯一的\u ptr用于某种类型: void FancyAlgo(unique_ptr<SomeType>& ptr); 当我需要用unique_ptr调用它时: FancyAlgo(*ptr); 同样,对于共享ptr 1、这是生产代码中可以接受的风格吗?(我在某个地方看到,在智能指针的上下文中,不应该以类似的方式操作原始指针。它有引入神秘bug的危险。) 2,如果1不是一个好主意,你能提出更好的方法(没有代码重复)吗 谢谢。智能指针

问题:

让我们假设我有一个算法,它将一个唯一的\u ptr用于某种类型:

void FancyAlgo(unique_ptr<SomeType>& ptr);
当我需要用unique_ptr调用它时:

FancyAlgo(*ptr);
同样,对于共享ptr

1、这是生产代码中可以接受的风格吗?(我在某个地方看到,在智能指针的上下文中,不应该以类似的方式操作原始指针。它有引入神秘bug的危险。)

2,如果1不是一个好主意,你能提出更好的方法(没有代码重复)吗


谢谢。

智能指针是关于所有权的。请求智能指针就是请求所有权信息或控制权

请求对智能指针的非常量左值引用就是请求更改该值的所有权状态的权限

请求对智能指针的常量左值引用就是请求查询该值的所有权状态的权限

请求对智能指针的右值引用就是一个“接收器”,并承诺从调用者那里夺走所有权

请求常量值引用是个坏主意

如果您正在访问指向的值,并且希望它不可为null,那么对底层类型的引用是好的

如果您希望它可以为空,可以使用
boost::optional
T*
以及
std::experimental
“世界上最笨的智能指针”(或同等的手写指针)。所有这些都是对某个变量的不可为空的引用

在界面中,不要要求你不需要的东西,也不要要求将来不需要的东西。这使得关于函数所做的事情的推理变得更加困难,并导致类似于OP中的问题。重置引用的函数与读取值的函数是完全不同的函数


现在,基于您的问题,一个更有趣的问题是,您希望函数重新放置智能指针,但您希望能够对共享和唯一的指针输入执行此操作。这是一种奇怪的情况,但我可以想象写一个类型擦除到emplace类型(一个
emplace\u sink

模板
使用later_ctor=std::函数;
模板
后期部署延迟部署(Args&…Args){
//依赖C++1z lambda引用绑定,手动编写
//如果这不能进入,或者不想依赖它:
返回[&](无效*ptr)->T*{
返回新的T(ptr)(std::forward(args));
};
}
命名空间详细信息{
模板
结构侵位目标{
虚拟~emplace_target(){}
虚拟T*emplace(以后的系数)=0;
};
}
模板
结构模板{
std::唯一的ptr pImpl;
模板
T*定位(Args&&…Args){
返回pImpl->emplace(延迟放置(std::forward(args)…);
}
模板
员工(标准::共享ptr和目标):
pImpl(新详细信息::emplace_shared_ptr(&target))//TODO
{}
模板
员工(标准::唯一性和目标):
pImpl(新详细信息::emplace_unique_ptr(&target))//TODO
{}
};
等等,需要大量的抛光。其思想是在任意上下文中键入对象
T
的擦除构造。我们可能需要使用特例
shared\u ptr
,这样我们就可以调用
make\u shared
,因为
void*->T*
延迟的ctor不足以实现这一点(不是根本上的,而是因为缺少API挂钩)

啊哈!我可以做一个没有特殊外壳的共享ptr


我们使用析构函数分配一块内存(
char[sizeof(T)]
),析构函数将缓冲区转换为
T
,然后调用该缓冲区中的delete就地构造(获取
T*
),然后通过
共享ptr(共享ptr,T*)
构造函数转换为
共享ptr
。小心例外,捕获这应该是安全的,我们可以使用定位函数将其定位到
make_shared
组合缓冲区中。

智能指针几乎不应该通过引用传递,除非您想谈论智能指针本身,而不是指针对象。使用
.get()
*
。为什么不呢?在旧的C++03中,传递指针是非常好的。现在,它只是成为拥有所有权的指针。我不明白为什么它会对你的决定产生影响。因为通常指针不是你感兴趣的对象,但指针对象是。如果你只是简单地修改传入的对象,你不需要麻烦使用智能指针——只需使用引用即可。如果参数可能是空的或无效的,请考虑使用诸如 Boo::可选的< /代码>这样的信息来保存这些信息,而不是使用指针空值作为指示器的旧实践。传入指针对象解决了这个问题。但是,在函数的唯一上下文中(假设另一个开发人员实现了此函数),根本没有任何东西可以提示实现开发人员他不应该使用另一个智能指针(例如,shared_ptr other_ptr(&value);)来管理指针对象。如果他这样做了,那么我们以后会有双重删除问题。如果函数签名有一个智能指针,那么这显然表明他不应该尝试使用另一个智能指针来管理对象。对于第一部分,我在OP下面留下了两条注释,以进一步讨论所有权的模糊性以及由此导致的潜在双重删除问题。第二部分,雇主是疯狂的,但我喜欢:p为什么我需要这样的雇主?我的理解是,您正在尝试实现虚拟构造函数机制(并将其设置为共享的\u ptr/unique\u ptr讨论的上下文),但在哪种情况下它是必要的和有用的?@h9uest是的,
T*
的所有权不明确。因此,世界上最愚蠢的智能指针--
std::experimental::observer_ptr
,它表示“我不拥有我指向的东西”,否则其行为就尽可能类似于
T*
T&
是不可为空的
void FancyAlgo(SomeType& value);
FancyAlgo(*ptr);
template<class T>
using later_ctor = std::function<T*(void*)>;

template<class T, class...Args>
later_ctor<T> delayed_emplace(Args&&...args) {
  // relies on C++1z lambda reference reference binding, write manually
  // if that doesn't get in, or don't want to rely on it:
  return [&](void* ptr)->T* {
    return new T(ptr)(std::forward<Args>(args));
  };
}

namespace details {
  template<class T>
  struct emplace_target {
    virtual ~emplace_target() {}
    virtual T* emplace( later_ctor<T> ctor ) = 0;
  };
}
template<class T>
struct emplacer {
  std::unique_ptr<emplace_target<T>> pImpl;
  template<class...Args>
  T* emplace( Args&&... args ) {
    return pImpl->emplace( delayed_emplace<T>(std::forward<Args>(args)...) );
  }
  template<class D>
  emplacer( std::shared_ptr<T, D>& target ):
    pImpl( new details::emplace_shared_ptr<T,D>(&target) ) // TODO
  {}
  template<class D>
  emplacer( std::unique_ptr<T, D>& target ):
    pImpl( new details::emplace_unique_ptr<T,D>(&target) ) // TODO
  {}
};