0

Example:

class Bar;

class Foo
{
public:

    Foo(const Bar& bar) : mBar(&bar) {}

    /* Other methods use mBar. */

private:

    const Bar* mBar;
};

So the goal is to store a const pointer to some external object, not to store a copy of the external object's contents.

  • Pass by const reference: Foo(const Bar& bar)
    • Common idiom to pass a object to a constructor, many programmers will think the Foo will store a copy of the Bar rather then the pointer/reference itself.
    • Caller can pass a temporary. This can be prevented by deleting the r-value overload Foo(const Bar&&) = delete. However this isn't fool-proof as you can still smuggle in temporaries via a function that takes a const reference and returns a const reference. Foo afoo(std::min(Bar{}, anotherBar)); for example.
  • Pass by const pointer: Foo(const Bar* bar)
    • Must check for nullptr, which is a runtime check, compile time mechanisms are preferred.
    • Can still smuggle in temporaries: Foo afoo(&std::min(Bar{}, anotherBar));

Which of the above is preferred and why? Or are there any better ways to do this that do not suffer from the above problems ?

Unimportant
  • 2,076
  • 14
  • 19
  • The most clear semantics could be expressesd with smart pointers IMO. – πάντα ῥεῖ Apr 20 '19 at 16:14
  • @πάνταῥεῖ But the `Bar` reference/pointer to be passed in need not necessarily be dynamically allocated. – Unimportant Apr 20 '19 at 16:19
  • This looks like an opinion-based question. So in my opinion, taking a pointer as parameter is the way to go, since that's what you're actually doing (storing a pointer to an object.) Document it must not be null and throw an exception in the constructor if it is null. Since `Foo` stores a pointer to an externally managed object, the user of `Foo` has to be responsible for avoiding breakage. I don't see a way around that. If that's not good enough, then the only option is to restructure your design so that no pointer to an external object is needed. – Nikos C. Apr 20 '19 at 16:50

1 Answers1

1

You can accept a ::std::reference_wrapper<Bar>, it will handle case with passing an rvalue as well as provide a hint that you are aming to copy wrapper rather than the object.

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • That suffers from the same problem as detailed in the question. One can still smuggle a temporary into the reference wrapper. `Foo afoo(std::cref(std::min(Bar{}, anotherBar)));`. But it is more descriptive indeed. – Unimportant Apr 20 '19 at 16:29