I'm trying to implement something like Rust's Result<T,E>
type in C++, which is a union that holds either T
or E
value.
Some of its constructors are:
template <typename T, typename E>
Result<T,E>::Result(const T& value) : isOk(true), value(value) {}
template <typename T, typename E>
Result<T,E>::Result(T&& value) : isOk(true), value(std::move(value)) {}
It works as I expect for T
and E
being non-reference types or pointers, but fails to compile if any of the underlying types is a reference. For example:
MyType my_object;
Result<MyType&, AnyOtherType> result(my_object);
produces the following error:
./result.h:46:5: error: multiple overloads of 'Result' instantiate to the same signature 'void (MyType &)'
Result(T&& value);
^
main.cpp:39:23: note: in instantiation of template class 'Result<MyType &, int>' requested here
Result<MyType&,int> result(object);
^
./result.h:37:5: note: previous declaration is here
Result(const T& value);
^
I understand that this is because of the reference collapsing rules (& + && = &): if T
is MyType&
, then T&
and T&&
are both MyType&
, hence those two constructors have the same signature here.
But is there any nice way to overcome this, and allow T
to be a reference, while still having both const T&
and T&&
constructors?