Reputation: 41
I have an Entity class with a private constructor and public CreateEntity function. CreateEntity stack-allocates new entities. In my app, I'd like to use a unique pointer to the returned entity. I found a solution with a temporary unique pointer and two move instructions. How do I write the two lines in Start() with make_unique and a single move instruction?
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);
}
Upvotes: 1
Views: 2546
Reputation: 8074
I may not have understood your needs correctly, but the sample code below:
Entity
object in such a way that every instance has a different ID.Entity
class implementation details. Before, you were creating an Entity
object with auto e = Entity::CreateInstance();
. Now, you can do the same with auto e = Entity{};
. That is, user knows nothing about IDs in both cases.Entity
by adding a default constructor to Entity
. Now, you can just initialize your Lucre::m_Object
member with m_Object{ std::make_unique<Entity>() }
.#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
Upvotes: 1
Reputation: 76824
Just replicating the behavior of the shown lines:
m_CameraObject = std::make_unique<Entity>(Entity::CreateEntity());
This will perform a single move construction of Entity
and a single move assignment of std::unique_ptr
(assuming m_CameraObject
is of type std::unqiue_ptr<Entity>
) in addition to the non-move/copy object constructions for Entity
and std::unqiue_ptr
(guaranteed in C++17 or later, otherwise likely).
The std::move
are not required in this, since both Entity::CreateEntity()
and std::make_unique<Entity>(Entity::CreateEntity())
are already prvalues. In your original the std::move
around Entity::CreateEntity()
is also redundant.
However it is difficult to tell without full code whether this is what you really want to do. It may be that letting the factory function return a std::unique_ptr<Entity>
directly is the better option. Then a move constructor call for Entity
wouldn't be required.
Upvotes: 2
Reputation: 7688
You can combine the 2 lines in Lucre::Start()
like this:
m_CameraObject.reset( new Entity( Entity::CreateEntity() ) );
Here, the return of Entity::CreateEntity()
is moved, but no std::move()
is needed as the return is already prvalue.
Upvotes: 2