Reputation: 91
#include <iostream>
using namespace std;
namespace mine {
template <typename T>
struct remove_rval {
using type = T;
};
template <typename T>
struct remove_rval<T&&> {
using type = T;
};
template <typename T>
void g(const T& = typename remove_rval<T>::type())
cout << __PRETTY_FUNCTION__ << endl;
}
}
int main()
{
mine::g<int&&>(); // doesn't work, because of explicit template?
const int& i2 = mine::remove_rval<int&&>::type(); // works, sanity check
return 0;
}
The function template I wrote fails to compile. From my understanding of c++, you can assign an rvalue to a constant lvalue reference. But, in this situation, it is like the deduced type disregards the 'const' qualifier when assigning the function default value. Why is this?
Upvotes: 2
Views: 284
Reputation: 1
From dcl.ref/p6:
If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.decltype]) denotes a type TR that is a reference to a type T, an attempt to create the type lvalue reference to cv
TR
creates the type lvalue reference toT
, while an attempt to create the type rvalue reference to cv TR creates the typeTR
.
Thus in your example, when T = int&&
:
const T&
collapses to T&
(which isint&
) and not const T&
(which is const int&
) according to the above quoted statement. And since we can't bind an rvalue like remove_rval<T>::type()
to a non-const lvalue reference, you get the mentioned error.
Thus, even though the form of the function parameter in g
is a reference to const T
aka const lvalue reference(i.e., const T&
), the first call to g
instantiates g
with a reference to non-const T
aka non-const lvalue reference(i.e., T&
=int&
) as the parameter:
template<>
void g<int &&>(int&)
{
//operator<< called here
}
And since the parameter is int&
, we cannot bind an rvalue like remove_rval<int&&>::type()
to that parameter.
Upvotes: 3
Reputation: 96790
void g(const T& = typename remove_rval<T>::type())
Reference collapsing rules make const T&
equal to U&
when T = U&&
. Lvalue-references can't bind to temporaries like remove_rval<T>::type()
. You can instead simply pass int
as a template argument and the parameter's type will correctly be const int&
.
Upvotes: 2