Seems like I don't understand something fundamental about the type deduction /reference collapsing rules of C++. Say I have an object entity
which takes by rvalue reference in the constructor and has a data member of the same type. I was fine with this until I learned that the deduced type is deduced according to reference collapsing rules, e.g.:
When I bind an xvalue Alloc&& to the parameter Alloc&& alloc, the deduced type of Alloc will be Alloc&& according to:
- A& & becomes A&
- A& && becomes A&
- A&& & becomes A&
- A&& && becomes A&&
So when the deduced type "Alloc" is actually Alloc&& in the following example, why is it that this class seems to store the value type Alloc rathern than the deduced rvalue reference? Shouldn't the class member type "Alloc" secretly be an rvalue reference, since im calling the ctor with an xvalue (std::move)?
#include <memory>
#include <cstdio>
#include <type_traits>
template <typename Alloc>
struct entity
{
entity(Alloc&& alloc)
: alloc_{ alloc }
{}
auto print() {
if constexpr (std::is_rvalue_reference_v<Alloc>) {
printf("Is type is &&");
} else if constexpr (std::is_lvalue_reference_v<Alloc>) {
printf("Is type is &");
} else {
printf("Is type value");
}
}
Alloc alloc_;
};
int main()
{
std::allocator<int> a;
entity e(std::move(a));
e.print();
}
Output:
Is type value
Alloc&&
isn't a forwarding reference as it's not used in a function template, it's just an rvalue reference. ThereforeAlloc
is deduced to bestd::allocator<int>
andalloc
in your constructor is an rvalue reference.To see forwarding references you need a function template. E.g.:
Note however that
Alloc
will still not be an rvalue reference, when passed an rvalueAlloc
deduces tostd::allocator
butalloc
is an rvalue reference:Prints: