【问题标题】:Create unique pointer with make_unique and move?使用 make_unique 创建唯一指针并移动?
【发布时间】:2022-01-25 23:50:24
【问题描述】:

我有一个带有私有构造函数和公共 CreateEntity 函数的 Entity 类。 CreateEntity 堆栈分配新实体。在我的应用程序中,我想使用指向返回实体的唯一指针。我找到了一个带有临时唯一指针和两个移动指令的解决方案。 如何使用 make_unique 和单个移动指令在 Start() 中编写两行?

class Entity
{
public:
    Entity(const Entity&) = delete;
    Entity& operator=(const Entity&) = delete;
    Entity(Entity&&) = default;
    Entity& operator=(Entity&&) = default;
    static Entity CreateEntity();
private:
    Entity(id_t id): m_ID(id) {}
private:
    uint32_t m_ID;
};
Entity Entity::CreateEntity()
{
    static id_t currentID = 0;

    return Entity{currentID++};
}

struct Lucre : public Application
{
    bool Start();
    std::unique_ptr<Entity> m_Object;
}
bool Lucre::Start()
{
    std::unique_ptr<Entity> ptr( new Entity(std::move(Entity::CreateEntity())));
    m_Object = std::move(ptr);
}

【问题讨论】:

  • 我不确定我是否理解,但是没有 move 指令。 Start() 方法看起来有点到处都是。它的目的是什么,从上到下... 1. 做什么,做什么? ...
  • 公开你的默认构造函数,不接收参数,在: m_ID{currentID++}使用成员初始化,currentID是一个静态成员,然后用m_Object = std::make_unique&lt;Entity&gt;();初始化唯一指针。
  • Ted,有一个错字现已修正。在我的代码中,它被称为 m_CameraObject。当我简化 StackOverflow 的代码时,我忽略了一个事件。 Rturrado,这是一个很好的替代方法,我将来可能会使用它。非常感谢!

标签: c++ move unique-ptr


【解决方案1】:

您可以像这样组合Lucre::Start() 中的两行:

m_CameraObject.reset( new Entity( Entity::CreateEntity() ) );

这里,Entity::CreateEntity() 的返回值被移动,但不需要std::move(),因为返回值已经是prvalue。

【讨论】:

  • 这里真的需要std::move吗? Entity::CreateEntity() 不是已经是prvalue了吗?
  • @Brian - 你是对的 - 会解决的。
【解决方案2】:

只是复制显示的行的行为:

m_CameraObject = std::make_unique<Entity>(Entity::CreateEntity());

除了Entity 的非移动/复制对象构造之外,这将执行Entity 的单个移动构造和std::unique_ptr 的单个移动分配(假设m_CameraObject 的类型为std::unqiue_ptr&lt;Entity&gt;)和std::unqiue_ptr(在 C++17 或更高版本中保证,否则可能)。

这里不需要std::move,因为Entity::CreateEntity()std::make_unique&lt;Entity&gt;(Entity::CreateEntity()) 都已经是prvalues。在您原来的 std::move 周围 Entity::CreateEntity() 也是多余的。

但是,如果没有完整的代码,很难判断这是否是您真正想要做的。让工厂函数直接返回std::unique_ptr&lt;Entity&gt; 可能是更好的选择。这样就不需要调用Entity 的移动构造函数了。

【讨论】:

  • 抱歉,我已将 m_CameraObject 重命名为 m_Object 以简化代码 - 已修复
  • m_Object.reset( new Entity(Entity::CreateEntity()));m_Object = std::make_unique&lt;Entity&gt;(Entity::CreateEntity()); 确实有效。这甚至比我现在寻找的更好,因为没有更多的移动指令。非常感谢大家!也谢谢你的解释!
【解决方案3】:

我可能没有正确理解您的需求,但是下面的示例代码:

  • 还以每个实例具有不同 ID 的方式实现 Entity 对象。
  • 仍然隐藏Entity 类的实现细节。之前,您使用auto e = Entity::CreateInstance(); 创建了一个Entity 对象。现在,您可以对auto e = Entity{}; 执行相同的操作。也就是说,在这两种情况下,用户对 ID 一无所知。
  • 通过向Entity 添加默认构造函数来简化Entity 堆实例的创建。现在,您可以使用m_Object{ std::make_unique&lt;Entity&gt;() } 初始化您的Lucre::m_Object 成员。

[Demo]

#include <cstdint>  // uint32_t
#include <iostream>  // cout
#include <memory>  // make_unique, unique_ptr

class Entity
{
public:
    Entity(): m_ID{currentID++} {}
    Entity(const Entity&) = delete;
    Entity& operator=(const Entity&) = delete;
    Entity(Entity&&) = default;
    Entity& operator=(Entity&&) = default;

    auto getID() const { return m_ID; }
    friend std::ostream& operator<<(std::ostream& os, const Entity& e) { return os << e.m_ID; }
private:
    static inline uint32_t currentID;
    uint32_t m_ID;
};

struct Lucre
{
    Lucre() : m_Object{ std::make_unique<Entity>() } {}

    friend std::ostream& operator<<(std::ostream& os, const Lucre& l) { return os << l.m_Object->getID(); }
private:
    std::unique_ptr<Entity> m_Object{};
};

int main()
{
    Lucre lucre1{};
    Lucre lucre2{};
    std::cout << "lucre1 = " << lucre1 << ", lucre2 = " << lucre2 << "\n";
}

// Outputs:
//
//   lucre1 = 0, lucre2 = 1

【讨论】:

  • 您确实正确理解了我想要实现的内容。如果没有 CreateEntity(),您的 Entity 类会更容易一些。非常感谢您的帮助!顺便说一句,我的项目可以在这里找到github.com/beaumanvienna/vulkan(参见 engine/scene/entity.h 和 application/lucre/lucre.h)
  • 很高兴能提供帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多