我正在尝试创建一个非空的unique_ptr
。
template <typename T>
class unique_ref {
public:
template <class... Types>
unique_ref(Types&&... Args) { mPtr = std::make_unique<T, Types...>(std::forward<Types>(Args)...); }
T* release() && { return mPtr.release(); }
T* release() & = delete;
private:
std::unique_ptr<T> mPtr;
};
我的目标是只有在release()
是临时的时候才允许unique_ref
。
问题是有人可以使用std::move()
“绕过”这个:
unique_ref<int> p;
int* p2 = std::move(p).release();
有没有办法阻止它成为move
'd?
就重载分辨率而言,没有办法区分prvalues(临时值)和xvalues(std::move
的结果)。
并且没有办法阻止std::move
将左值转换为x值。
release
不是非null保证“唯一指针”可以支持的操作。也没有移动建设/任务。据我所知,保证的唯一方法是使指针不可移动,并使复制操作分配深拷贝。
你将不得不让std::move
案件去。当用户调用std::move
时,他们会发出一个强烈信号,表明他们确切知道自己在做什么。
您可以在调试期间保护自己。
例如,我会考虑启动类定义,如下所示:
#include <memory>
#include <cassert>
template <typename T>
class unique_ref {
public:
// a number of problems here, but that is a discussuion for another day
template <class... Types>
unique_ref(Types&&... Args)
: mPtr(std::make_unique<T>(std::forward<Types>(Args)...))
{ }
// unique_ref is implicitly move-only
// see check below
bool has_value() const {
return bool(mPtr);
}
// here I am implicitly propagating the container's constness to the
// inner reference yielded. You may not want to do that.
// note that all these accessors are marshalled through one static function
// template. This gives me control of behaviour in exactly one place.
// (DRY principles)
auto operator*() -> decltype(auto) {
return *get_ptr(this);
}
auto operator*() const -> decltype(auto) {
return *get_ptr(this);
}
auto operator->() -> decltype(auto) {
return get_ptr(this);
}
auto operator->() const -> decltype(auto) {
return get_ptr(this);
}
private:
using implementation_type = std::unique_ptr<T>;
implementation_type release() { return std::move(mPtr); }
// this function is deducing constness of the container and propagating it
// that may not be what you want.
template<class MaybeConst>
static auto get_ptr(MaybeConst* self) -> decltype(auto)
{
auto ptr = self->mPtr.get();
assert(ptr);
using self_type = std::remove_pointer_t<decltype(self)>;
if constexpr (std::is_const<self_type>())
return static_cast<T const*>(ptr);
else
return ptr;
}
private:
implementation_type mPtr;
};
struct foo
{
};
auto generate()->unique_ref<foo> {
return unique_ref<foo>();
}
void test()
{
auto rfoo1 = generate();
auto rfoo2 = generate();
// auto rfoo3 = rfoo1; not copyable
// we have to assume that a user knows what he's doing here
auto rfoo3 = std::move(rfoo1);
// but we can add a check
assert(!rfoo1.has_value());
auto& a = *rfoo3;
static_assert(!std::is_const<std::remove_reference_t<decltype(a)>>());
const auto rfoo4 = std::move(rfoo3);
auto& b = *rfoo4;
static_assert(std::is_const<std::remove_reference_t<decltype(b)>>());
}