Code on gcc.godbolt.org.
I created a simple type trait to remove rvalue references:
template <typename T>
struct remove_rvalue_reference { using type = T; };
template <typename T>
struct remove_rvalue_reference<T&&> { using type = T; };
template <typename T>
using remove_rvalue_reference_t =
typename remove_rvalue_reference<T>::type;
I'm using it to implent a copy_if_rvalue(x)
function whose return type depends on the passed argument:
template <typename T>
constexpr auto copy_if_rvalue(T && x)
-> remove_rvalue_reference_t<decltype(std::forward<decltype(x)>(x))>
{
return std::forward<decltype(x)>(x);
}
To make sure the function returns the correct types, I've written some simple static assertions:
// literal
static_assert(std::is_same<
decltype(copy_if_rvalue(0)), int
>{});
// lvalue
int lv = 10;
static_assert(std::is_same<
decltype(copy_if_rvalue(lv)), int&
>{});
// const lvalue
const int clv = 10;
static_assert(std::is_same<
decltype(copy_if_rvalue(clv)), const int&
>{});
// rvalue
int rv = 10;
static_assert(std::is_same<
decltype(copy_if_rvalue(std::move(rv))), int
>{});
// volatile lvalue
volatile int vlv = 10;
static_assert(std::is_same<
decltype(copy_if_rvalue(vlv)), volatile int&
>{});
// const lvalue
volatile const int vclv = 10;
static_assert(std::is_same<
decltype(copy_if_rvalue(vclv)), volatile const int&
All the above static assertions compile successfully. When trying to std::move
a volatile int
variable, though, something unexpected occurs:
// volatile rvalue
volatile int vrv = 10;
// (0) fails:
static_assert(std::is_same<
decltype(copy_if_rvalue(std::move(vrv))), volatile int
>{});
// (1) unexpectedly passes:
static_assert(std::is_same<
decltype(copy_if_rvalue(std::move(vrv))), int
>{});
// (2) unexpectedly passes:
static_assert(std::is_same<
remove_rvalue_reference_t<decltype(std::forward<decltype(vrv)>(std::move(vrv)))>,
volatile int
>{});
Assertion (0) fails - the volatile
does not propagate, as demonstrated by assertion (1).
However, assertion (2) passes, even if I think it should be equivalent to assertion (0), since copy_if_rvalue
's return type is exactly the same as (2)'s first type:
// (from assertion (2))
remove_rvalue_reference_t<decltype(std::forward<decltype(vrv)>(std::move(vrv)))>
// ...should be equivalent to...
// (from copy_if_rvalue)
-> remove_rvalue_reference_t<decltype(std::forward<decltype(x)>(x))>
It seems that volatile
is not propagated only when std::move
is used and only through the copy_if_rvalue
template function.
What is going on here?
There are no cv-qualified scalar prvalues. [expr]/6:
If a prvalue initially has the type “cv
T
”, whereT
is a cv-unqualified non-class, non-array type, the type of the expression is adjusted toT
prior to any further analysis.
I.e. the same rule giving
int const f();
f() // <=
type int
applies here as well. If you try some class type instead of int
(e.g. std::string
), you will get the expected types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With