Reputation: 23
What is a way to define a member object variable of a class that can either be
Basically I would like to have something like this (which does not compile due to trying to bind non-const lvalue reference to temporary):
class A;
class B{
public:
B() : m_a { A() } {} // <- does not compile because A() is temporary
B(A& a) : m_a { a } {}
useA() { m_a.do_something(); }
private:
A& m_a;
};
const
reference is not an option, since I need access to non-const
methods of the object.
std::unique_ptr
is not an option, because ownership shall stay with the caller of the class's constructor.
Is one of these following options the most reasonable thing to do or is there a better way? Both seem kind of weird. Creating an object m_aObj
that is not at all used if the second constructor is used. And using shared_ptr
s even though I do not want to keep ownership necessarily.
class A;
class B{
public:
B() : m_a { m_aObj } {}
B(A& a) : m_a { a } {}
useA() { m_a.do_something(); }
private:
A& m_a;
A m_aObj;
};
class A;
class B{
public:
B() : m_a { std::make_shared<A>() } {}
B(std::shared_ptr<A> a) : m_a {a} {}
useA() { m_a->do_something(); }
private:
std::shared_ptr<A> m_a;
};
Upvotes: 1
Views: 130
Reputation: 218118
And with the other smart pointer, std::shared_ptr
which might type-erase the deallocator:
class B{
public:
B() : m_a(std::make_shared<A>()) {}
B(A& a) : m_a { &a, [](auto*){/*No-op deleter*/} } {}
B(const B&) = delete;
B& operator=(const B&) = delete;
void useA() { m_a->do_something(); }
private:
std::shared_ptr<A> m_a;
};
Upvotes: 1
Reputation: 535
Alternative solution to std::optional
, trading off heap allocation on the empty path for much smaller size (two pointers max):
class B {
public:
B() : smart{ std::make_unique<A>() } ()
B(A& a) : raw{ &a } ()
A* operator->() { smart ? smart.get() : raw; }
void useA() { (*this)->doSomething(); }
private:
std::unique_ptr<A> smart{};
A* raw{};
};
Upvotes: 2
Reputation: 63947
You can combine a reference A&
with an optional internal std::optional<A>
.
class B{
public:
B()
: m_internal_a{ std::in_place } // Create the internal A
, m_a { *m_internal_a } // ... and reference it
{
}
B(A& a)
: m_internal_a{ std::nullopt } // Omit the internal A
, m_a { a } // ... and reference the external one.
{
}
void useA() { m_a.do_something(); }
private:
std::optional<A> m_internal_a; // Used only when A is internal
A& m_a;
};
Upvotes: 5