I'm sick of passing all my objects by value and then moving, or overloading on lvalues and rvalues. I'm trying to write a class that can either store an lvalue reference or a normal object if an rvalue is passed into the constructor.
Here is the skeleton:
template<typename T>
class ref {
private:
T* m_ptr = nullptr;
std::optional<T> m_obj;
bool is_lvalue = true;
public:
ref(T& lvalue_ref) : m_ptr{ &lvalue_ref } {}
ref(T&& rvalue_ref) : m_obj{ std::move(rvalue_ref) }, is_lvalue{ false } {}
T& get() {
return (is_lvalue) ? *m_ptr : *m_obj;
}
};
I want to use this class with functions like void foo(ref<bar> r) {}, so any function taking ref<T> does not make any copies. Unfortunately, you have to check every time if a reference/object is stored whenever you get the value, and std::optional default constructs its object and we pay for that even if we are storing an lvalue. Is it possible to create a class that stores either an lvalue reference, or an object, and still be able to have a function taking ref<T> be able to bind both lvalues and rvalues of T to it?
I'll first quote my comment, so everybody can have a full view of my... view.
But I still think some guessing can be attempted.
From
and
I assume you have many functions taking args by value, and/or many functions overloaded for
&and&&, in either case the type(s) of the arg(s) being concrete (otherwise, if those functions were templated, I'd have expected at least a mention of a forwarding reference in the body of your question).To avoid the copies, I think you'd go (or you are going already) for a solution like this, which is repeated for several functions, where you duplicate not just the interfaces in a header,
but also the business logic in the implementation files:
a typical client code looking like this,
If that's the case, one solution could be to just template the functions and impletement them in the cpp file, together with the two explicit instantiations you need:
Notice that my toy example has the same length in both cases, but there are some differences between the two. In my proposed solution,
std::forward<T>the argument to other functions, whereas in the "manual" overload case you have to writestd::movein one overload and nothing in the other;foowith a simpler type thanBarby just instantiatingfoofor aBarForTestsmock type.