I have a function address_of, which returns a Pointer (encapsulating a shared_ptr) to its argument. address_of needs to work on both lvalues and rvalues, so there are two versions of address_of: one accepting a reference and the other accepting an rvalue reference. Since taking the address of a temporary is a Bad Thing™, the rvalue version of address_of needs to perform a move-construct in order for the Pointer to actually own something. The implementation is straightforward:
template<class T>
inline Pointer address_of(T& value) {
return Pointer(&value);
}
template<class T>
inline Pointer address_of(T&& value) {
return Pointer(new T(std::move(value)));
}
And taking the address of a temporary works as expected:
Pointer p = address_of(Derived());
But when I test with the following code:
Base* object = new Derived();
Pointer p = address_of(*object);
GCC complains that the call to address_of is ambiguous:
error: call of overloaded ‘address_of(Base&)’ is ambiguous
note: candidates are: Pointer address_of(T&) [with T = Base]
note: Pointer address_of(T&&) [with T = Base&]
I was under the impression that unary * always returns an lvalue, in which case the rvalue version shouldn’t even be considered. What exactly is going on here?
The problem is caused by reference decay: (correct term is “reference collapse”)
The answer to the question in the code is that f() is:
Which because of reference decay turns into this:
As you can expect, this will of course be ambiguous with anything defined as:
This might work (fixed version):
The reason being that this reference decay stuff only happens in functions that are templated on T. In this case your pointer class is, but the constructors are not actually templates themselves and so T& is never a valid T for the T&& version.
The first version still had the same problem as your code since address_of was just using T as the template parameter for pointer. We actually need the raw type.