防止 std::在对象上移动

Prevent std::move on object?

本文关键字:移动 对象 std 防止      更新时间:2023-10-16

我正在尝试创建一个非空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;
};

我的目标是仅在unique_ref是临时的情况下才允许release()

问题是有人可以使用std::move()来"绕过"这个问题:

unique_ref<int> p;
int* p2 = std::move(p).release();

有没有办法防止它被move

重载分辨率而言,没有办法区分 prvalue(临时(和 x值(std::move 的结果(。

并且没有办法阻止std::move将左值转换为 x值。

release不是非空保证"唯一指针"可以支持的操作。移动建设/分配也不是。据我所知,做出保证的唯一方法是使指针不可移动,并使复制操作分配一个深拷贝。

>你将不得不放弃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)>>());
}