使用 std::Optional 使移动构造函数/分配中的 RAII 对象无效

Joh*_*ien 2 c++ raii c++20 stdoptional

假设我有一个 RAII 类,其实例永远不应该被复制:

class Session {

public:
    Session(); // Allocates a resource and sets generates a unique Session::id. 
    ~Session(); // Frees the resource

    Session(const Session&) = delete;
    Session& operator = (Session&) = delete;

private:
  std::uint32_t id;
}
Run Code Online (Sandbox Code Playgroud)

鉴于我不能允许复制,我想允许移动,所以我需要实现移动构造函数和移动赋值运算符,但我不确定应该如何处理Session::id移动的实例。

我可以:

  1. 将其设置为某个已知无效的值(可能将类型更改为有符号 int 并用作-1无效值)
  2. 使用类似的东西std::optional并将其设置为std::nullopt使其无效。

这些选项中哪一个(如果有的话)是正确的?

Ted*_*gmo 6

std::optional由于这可以通过牺牲 2 32个可能的s 中的一个值来完成id,因此我确实希望使用std::string::npos来初始化一个无符号类型的常量。id-1

例子:

class Session {
public:
    // the constant used to signal an invalid id:
    static constexpr std::uint32_t invalid_id = static_cast<std::uint32_t>(-1);

    Session() :
        id(id_counter++)
        /* allocate the resource */
    {}
    Session(const Session&) = delete;
    Session(Session&& other) noexcept :
        id(std::exchange(other.id, invalid_id)) // exchange with the invalid id
        /* move or exchange the resource */
    {}
    Session& operator=(const Session&) = delete;
    Session& operator=(Session&& other) noexcept {
        // check for self-assignment if needed
        std::swap(id, other.id);
        // swap the resource
        return *this;
    }
    ~Session() {
        if(id != invalid_id) { // if the check is even needed
            /* free the resource */
        }
    }

private:
    inline static std::uint32_t id_counter = 0;
    std::uint32_t id;
};
Run Code Online (Sandbox Code Playgroud)

演示

一个选择是让0ifinvalid_id在您的情况下感觉更自然。您只需使用 进行初始化invalid_id并将0id 计数更改为id(++id_counter)