[英]Prevent std::move on object?
I'm trying to create a not-null unique_ptr
. 我正在尝试创建一个不为空的
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;
};
My goal is to allow release()
only if the unique_ref
is a temporary. 我的目标是仅当
unique_ref
是临时的时才允许release()
。
The problem is someone could use std::move()
to "get around" this: 问题是有人可以使用
std::move()
来解决这个问题:
unique_ref<int> p;
int* p2 = std::move(p).release();
Is there a way to prevent it from being move
'd? 有没有办法防止它被
move
?
There is no way of distinguishing prvalues (temporaries) from xvalues (result of std::move
) as far as overload resolution is concerned. 就重载解析而言,无法将prvalues(临时)与xvalues(
std::move
结果)区分开。
And there is no way of preventing std::move
from converting an lvalue to an xvalue. 而且没有办法阻止
std::move
将左值转换为左值。
release
is not an operation that can be supported by a non-null-guarantee "unique pointer". release
不是非null保证“唯一指针”可以支持的操作。 And neither is move construction / assignment. 移动构造/分配也不是。 As far as I can tell, the only way to make the guarantee is to make the pointer non-movable, and make the copy operation allocate a deep copy.
据我所知,保证的唯一方法是使指针不可移动,并使复制操作分配一个深拷贝。
You're going to have to let the std::move
case go. 您将不得不放开
std::move
案例。 When a user invokes std::move
, they are giving a strong signal that they know exactly what they are doing. 当用户调用
std::move
,他们会发出强烈的信号,表明他们确切地知道自己在做什么。
You can protect yourself though during debug time. 您可以在调试期间保护自己。
For example, I would consider starting the class definition a little like this: 例如,我会考虑像这样开始类定义:
#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)>>());
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.