Model custom type as pointer or as reference?

128 views Asked by At

In addition to pointers, C++ also provides references that behave similarly. In addition to these built-in types, it also gives the option to construct custom types that mimic this behavior.

Take these two custom types:

class Ptr
{
private:
    int inner_;

public:
    int& operator*() { return inner_; }
    const int& operator*() const { return inner_; }
};
class Ref
{
private:
    int inner_;

public:
    operator int&() { return inner_; }
    operator const int&() const { return inner_; }
};

These two types are used in different ways:

void f(const int& x);
auto r = Ref{};
auto p = Ptr{};
f(r);
f(*p);

These two are not equivalent but serve largely the same purpose. In the C++ standard library, example types are:

  • pointer behavior: std::optional, std::unique_ptr
  • reference behavior: std::reference_wrapper, std::atomic

In case we are designing a custom data type for which both pointer behavior and reference behavior are reasonable, which one should be chosen?

2

There are 2 answers

0
Aykhan Hagverdili On

C++ doesn't allow overloading operator.. So, whatever you do it is not possible to achieve ref.foo() syntax in a generic way. std::reference_wrapper, for instance, uses ref.get().foo() syntax, which is rather convoluted.

Aside from that, implicit conversions you have in your examples are error prone and generally not desirable.

Considering these, I would design my custom class to act like a pointer, because that is fully achievable with the current language rules.

0
Post Self On

One difference between the two models is how operators behave.

You can have Ptr{} + Ptr{} behave distinctly from *Ptr{} + *Ptr{}.

This is not true for Ref{} + Ref{} as here, an implicit conversion takes place and it would thus be very error prone if operator+ on Ref were to behave differently than on its element type.

In general, with Ptr{} what is modeled is a has-a relationship: the inner value is independent from the wrapper. Ref{} on the contrary, models an is-a relationship: the wrapper is meant to inherently behave the same way as the inner value.