使用variant + reference_wrapper + unique_ptr来表达(非)拥有资源的想法

时间:2018-01-03 09:57:10

标签: c++ c++17

如果我需要表达这样的想法: 有一个场景节点(带有变换)可以指向(不拥有)几何体(例如网格), 但有时节点拥有几何体非常方便。 以这种方式表达这个想法是否可以?如果没有 - 你能否就如何表达这个想法提出一些想法?

struct Node
{
    variant<reference_wrapper<SceneObject>, unique_ptr<SceneObject>> sceneObject{nullptr};
};

Node node1;
node1.sceneObject = make_unique<Mesh>(); // node is responsible for the life of the mesh

Node node2;
node2.sceneObject = sceneObjectDatabase.getResource("myMesh"); //the node does not own the mesh, but simply references it

Node node3;
node3.sceneObject = nullptr; // doesn't own any geometry

2 个答案:

答案 0 :(得分:2)

我不喜欢variant在您的特定示例中,即在一个可以直接访问成员的简单结构中。虽然它为您提供了所有权的灵活性,但这种灵活性会渗透到Node::sceneObject的每次使用中。

变体是特殊所有权语义的实现细节。它应该与sceneObject的使用分离,因为所有权不是调用者的关注点。然而,variant解决方案紧密耦合,因为调用者必须为拥有和非拥有sceneObject做不同的事情。

我喜欢@n.m的想法。将unique_ptr与自定义删除器一起使用。它为您提供了所有权的灵活性和访问sceneObject的一致方式。添加make_owning_sceneObject()make_non_owning_sceneObject()函数,您就拥有了一个简单的API。 此外,该解决方案似乎对性能处罚的可能性最小。访问始终是通过指针的简单间接,根本没有variant机制。当然,关于未测量的性能推测的常见警告适用。

答案 1 :(得分:2)

此处不要使用变体。你没有不同的类型,你有一种类型 - 但只有一种类型的属性。所以你可以写一个可能拥有的类型,基本上是T*bool

template <typename T>
class maybe_owning_ptr {
    T* ptr_;
    bool owns_;

public:
    maybe_owning_ptr() : ptr_(nullptr), owns_(false) { }
    maybe_owning_ptr(T* p, bool owns) : ptr_(p), owns_(owns) { }
    ~maybe_owning_ptr() {
        if (owns_) delete ptr_;
    }

    maybe_owning_ptr(maybe_owning_ptr&& rhs) noexcept
        : ptr_(std::exchange(rhs.ptr_, nullptr))
        , owns_(std::exchange(rhs.owns_, false))
    { }

    maybe_owning_ptr& operator=(maybe_owning_ptr&& rhs) noexcept
    {
        T* p = std::exchange(rhs.ptr_, nullptr);
        bool o = std::exchange(rhs.owns_, false);

        if (owns_) delete ptr_;
        ptr_ = p;
        owns_ = o;
        return *this;
    }

    // accessors here... get(), operator*, operator->(), etc.
};

请注意,我们不能拥有复制构​​造函数 - 因为我们不知道在拥有所有权的场景中该怎么做。

一直想用std::exchange()来表达某些事情......