I'm working on a templated class, and given an incoming type T
the template needs to map to a "safe" const reference. (This is essentially to avoid handing the ability to mutate to a wrapped function when it's called; see here for the real code).
The original author wrote something equivalent to this:
template <typename T>
using SafeReference =
std::conditional_t<std::is_scalar_v<T>, T, const T&>;
The scalar part isn't interesting here, but what's interesting is const T&
. This looks right to me, but it's not:
struct Foo{};
using Bar = Foo&&;
// A failing static assert, and a reduced version. It turns out `const Bar&`
// is `Foo&`.
static_assert(std::is_same_v<const Foo&, SafeReference<Bar>>);
static_assert(std::is_same_v<const Foo&, const Bar&>);
I understand that Foo&&
becomes Foo&
due to the rules for reference collapsing. I also understand that the const
is probably "lost" because it tries to make a reference const rather than making the referred-to type const. But I don't even know what to Google in order to confirm that.
Which part of the standard says that the const
is "lost" in an expression like const T&
for a templated T
expanding to an rvalue reference?
CodePudding user response:
Which part of the standard says that the const is "lost" in an expression like const T& for a templated T expanding to an rvalue reference?
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
.
The quote probably explains the behavior you're seeing.