问题:
假设我有一个算法将unique_ptr带到某种类型:
void FancyAlgo(unique_ptr<SomeType>& ptr);
现在我将shared_ptr sPtr改为SomeType,我需要在sPtr上应用相同的算法。这是否意味着我必须为shared_ptr复制算法?
void FancyAlgo(shared_ptr<SomeType>& sPtr);
我知道智能指针带有堆上底层托管对象的所有权。在我的FancyAlgo中,所有权通常不是问题。我想要剥离智能指针层并执行以下操作:
void FancyAlgo(SomeType& value);
当我需要使用unique_ptr调用它时:
FancyAlgo(*ptr);
同样适用于shared_ptr。
1,这在PRODUCTION代码中是否可以接受?(我在某个地方看到,在智能指针的上下文中,你不应该以类似的方式操纵原始指针。它有引入神秘错误的危险。)
2,如果1不是个好主意,你能否提出更好的方法(没有代码重复)。
感谢。
答案 0 :(得分:1)
智能指针是关于所有权的。要求智能指针是要求所有权信息或控制。
要求对智能指针的非常量左值引用要求允许更改该值的所有权状态。
要求对智能指针的const左值引用要求获得查询该值的所有权状态的权限。
要求对智能指针的右值引用是&#34; sink&#34;,并承诺将该所有权从调用者手中夺走。
要求const rvalue参考是一个坏主意。
如果您正在访问指向的值,并且您希望它不可为空,则对基础类型的引用是好的。
如果您希望它可以为空,可以接受boost::optional<T&>
或T*
,std::experimental
&#34;世界上最愚蠢的智能指针&#34; (或等同的手写的)。所有这些都是对某些变量的非拥有可空引用。
在界面中,不要问你不需要的东西,将来也不需要。这使得函数更难以推理,并导致像你在OP中遇到的问题。重新设置引用的函数与读取值的函数是完全不同的函数。
现在,基于你的一个更有趣的问题是你希望重置智能指针的功能,但是你希望能够对共享和唯一指针输入这样做。这是一种奇怪的情况,但我可以想象写一个类型 - 擦除 - 降格 - 类型(emplace_sink<T>
)。
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
{}
};
等。需要大量的抛光。我们的想法是将对象T
的构造擦除到任意上下文中。我们可能需要特殊情况shared_ptr
,因此我们可以调用make_shared
,因为void*->T*
延迟的ctor不足以解决这个问题(不是从根本上,但由于缺少API挂钩)
啊哈!我可以做一个make shared shared ptr而不需要特殊的套管。
我们使用析构函数分配一块内存(char[sizeof(T)]
),该缓冲区将缓冲区转换为T
,然后在该缓冲区中调用delete,就地构造(获取T*
),然后通过shared_ptr<T>
构造函数转换为shared_ptr<T>( shared_ptr<char[sizeof(T)]>, T* )
。通过仔细的异常捕获,这应该是安全的,我们可以使用我们的安置功能进入make_shared
组合缓冲区。