I created a simple custom refernce_wrapper class template, which, in contrast to std::reference_wrapper, has some additional functionality:
- It recognizes an empty state (which allows deferring binding).
It provides a rebinding option.(As noted in comments, this is actually possible with the copy assignment operator ofstd::reference_wrapper.)- 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:
- I don't care about binding rvalues.
- 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?
- I don't like raw pointers to be used in high-level application code.
- 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.
std::reference_wrapper. \$\endgroup\$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, whileoptionalneeds an additional flag). Second, the access to the members of the referenced object requiresrw->get().f()instead of simplerw->f(). \$\endgroup\$std::optional<std::reference_wrapper<T>>either; I’d wait for C++26 and just usestd::optional<T&>(or useboost::optional<T&>, or some such, then switch tostd::optional<T&>in the future). But the point is that whatever you make should look like an optional reference… because that’s literally what you’re describing. It should not be a “potentially broken reference that sometimes looks like a pointer”. You should make anoptional_reference<T>that behaves like aoptional<T&>, with your optimizations. \$\endgroup\$optional_reference, it sounds good :) \$\endgroup\$