kfpgk
kfpgk

Reputation: 23

C++ How to define a member variable that is either passed into or constructed within a constructor

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_ptrs 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

Answers (3)

Jarod42
Jarod42

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;
};

Demo

Upvotes: 1

Dominik Kaszewski
Dominik Kaszewski

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

Drew Dormann
Drew Dormann

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

Related Questions