3
\$\begingroup\$

I created a simple custom refernce_wrapper class template, which, in contrast to std::reference_wrapper, has some additional functionality:

  1. It recognizes an empty state (which allows deferring binding).
  2. It provides a rebinding option. (As noted in comments, this is actually possible with the copy assignment operator of std::reference_wrapper.)
  3. It defines operator->() for direct access to members of the referenced object.

Here is a simple code that illustrates this functionality:

Derived d;

reference_wrapper<Base> rw;  // impossible with std::reference_wrapper
rw = d;                      // deferred binding
rw->memfn();                 // 'rw.get().memfn()' with std::reference_wrapper

My implementation is as follows:

template <typename T>
class reference_wrapper
{
  T* ptr_;

public:
  reference_wrapper() : ptr_(nullptr) { }
  reference_wrapper(const reference_wrapper&) = default;

  template <typename U>
  reference_wrapper(U& obj) : ptr_(&obj) { }

  reference_wrapper& operator=(const reference_wrapper&) = default;

  template <typename U>
  reference_wrapper& operator=(U& obj)
  {
    ptr_ = &obj;
    return *this;
  }

  bool empty() const { return ptr_ == nullptr; }

  T& get() const
  {
    assert(!empty());
    return *ptr_;
  }

  operator T&() const { return get(); }

  T* operator->() const 
  {
    assert(!empty());
    return ptr_;
  }

  void clear() { ptr_ = nullptr; }
};

template <typename T>
inline auto ref(T& r)
{
  return reference_wrapper<T>(r);
}

template <typename T>
inline auto cref(const T& r)
{
  return reference_wrapper<const T>(r);
}

Complete live demo link: https://godbolt.org/z/MGYnWKo7o

Notes:

  1. I don't care about binding rvalues.
  2. For performance reasons, I want to avoid unnecessary branching in production builds. This is why the access to the referenced value is protected with assertions.

Rationale:

Why am I not using a pointer instead?

  1. I don't like raw pointers to be used in high-level application code.
  2. I want the access to the referenced object to be protected with assertions.

I will appreciate any comments/suggestions for improvement if anyone finds anything wrong with this code.

New contributor
Daniel Langr is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Pretty sure you can rebind std::reference_wrapper. \$\endgroup\$
    – indi
    Commented 12 hours ago
  • \$\begingroup\$ I should also point out: godbolt.org/z/s4bqG137Y \$\endgroup\$
    – indi
    Commented 11 hours ago
  • \$\begingroup\$ @indi You're right, the rebinding is possible, just not directly, but via the copy assignment operator. As for std::optional<std::reference_wrapper<T>>, I don't like this option at all. First, it is memory-inefficient (the internal pointer can recognize the empty state by itself, while optional needs an additional flag). Second, the access to the members of the referenced object requires rw->get().f() instead of simple rw->f(). \$\endgroup\$ Commented 54 mins ago

1 Answer 1

4
\$\begingroup\$

Correctness

Your assignment and construction templates are overly broad. You need sfinae or a requires clause to block reference_wrapper<T>, even if you don't care about dangling rvalues. Which you should.

Naming

What you have is a checked pointer. It is very surprising to have a type with reference in it's name be nullable, especially because it shares a name with something in std.

clear could be named reset, to match smart pointers.

\$\endgroup\$
1
  • \$\begingroup\$ I agree with all of that. I realized that clear does not make much sense here, and reset also matches std::optional. As for naming, it should be different. I just don't want it to contain "pointer", since it is actually somewhere in between both. Aa s pointer, it has an empty state, and operator->(). However, it also provides a cast to T&, which is not a pointer-thing. Also, it is initialized/assigned to by objects, not pointer arguments. \$\endgroup\$ Commented 48 mins ago

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.