Are there any downsides to using `std::reference_wrapper<T>` as an always-valid member variable instead of a pointer?

Viewed 205

I am having a quite common problem. I have a class that must store a non-owning pointer to a different class object.

I know that:

  • The lifetime of the reference object is guaranteed to outlive the instance.
  • The referenced object is passed in a constructor and does not change with the exception of moves or assignment.
  • It is never invalid.
  • It is used in many methods.
  • It can be shared by many instances.

Think of e.g. a logger class which is not global.

These points lead me to this solution using a reference variable which guarantees validity:

struct Foo{};

struct Bar{
    Bar(Foo& foo):m_foo(foo){}

Foo& m_foo;
};

The big downside is Bar is unnecessarily almost immutable - no assignment, no move.

The usual thing I did was to store Foo as a pointer instead. This solves most of the issues except that it is no longer very clear that the pointer is always valid. Furthermore it adds a small new issue that it can be invalidated in any method, which should not happen. (Making it const has the same downside as &). That makes me add assert(m_foo) to every method for peace of mind.

So, I was thinking about just storing std::reference_wrapper<Foo>. It is always valid and it keeps Bar mutable. Are there any downsides compared to a simple pointer? I know that any method can still point it to e.g. a local variable but let's say that does not happen because it is perhaps hard to obtain a new valid instance of Foo. At least it is harder than simple =nullptr;

I know this approach is used for containers like std::vector so I assume it is okay, but I would like to know if there is any catch I should look for.

1 Answers

Since Foo is a struct you need to invoke get() every time you access any member or field of Foo. With reference or pointer you could use '.' or '->' respectively for member access. So reference_wrapper is not "transparent" in that regard. (There is currently also no way of making it "transparent" in C++, which would be nice of course).

There will be no runtime overhead but code will be congested with get() calls.

If that is not of a concern for you then there are no downsides of using reference_wrapper instead of a pointer. (In fact reference_wrapper is implemented by using a pointer member)

EDIT: also if you only need to call one or two member functions of Foo one could inherit from reference_wrapper and add a call stub. But that perhaps may be overkill ...

Related