I would like to understand why a const volatile reference cannot be bound to an rvalue reference? What is the rational reason to prohibit such a conversion?
In the following code I comment out lines that do not compile:
int main(){
int i=1;
//const volatile int& cvi2=std::move(i); -> ERROR: why?
const volatile int i2=0;
//const volatile int& cvi3=std::move(i2);// -> ERROR: why?
}
Here a more realistic scenario, that fails to compile for a similar reason:
#include<iostream>
template<class T> void g(const T& b){
//do usefull things
}
template<class T,class F> void f(T& a,F a_func){
//do usefull things with a
a_func(std::move(a));
}
int main(){
int i=0;
volatile int vi=1;
f(i,g<int>); //OK no error;
f(vi,g<volatile int>);//ERROR: can not convert volatile int to
//const volatile int &
}
In this code, I would have expected that g<volatile int>(const volatile&)
accept any argument.
An other edit, for a more concret example:
#include <vector>
using usefull_type=int;
void set_communication_channel_to(volatile usefull_type* g,size_t n);
int main(){
auto vect=
std::vector<volatile usefull_type>(10,usefull_type{42});//->ERROR no known conversion
// from int to const volatile int &
set_communication_channel_to(vect.data(),vect.size());
//...
//...
}
There must have a good reason for this limitation no?
The correct question should sound as "Why a const volatile reference cannot be bound to an rvalue?"
The following code doesn't compile either, although no rvalue references are directly involved:
const volatile int& cvr = 0;
This answer to a related question cites the relevant section of the standard:
Per [dcl.init.ref]/5, for a reference to be initialized by binding to an rvalue, the reference must be a
const
non-volatile
lvalue reference, or an rvalue reference:— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const
), or the reference shall be an rvalue reference.
My guess is that this restriction has historical roots in the C++98 standard where rvalues were limited to temporaries, that were fully managed by the compiler. The compiler can place the temporary at any address or register of its choice, and treating it as a volatile object with observable reads doesn't make sense. In the new standard an lvalue reference can be converted to an rvalue reference with std::move()
, however as a result it gets the old properties assumed for rvalues, i.e. that their exact memory address is insignificant, and thus cannot have the volatile attribute assigned to it.
Technically this restriction is not a very limiting one, since you can effectively bind a const volatile
reference to an rvalue through an extra level of indirection:
// This doesn't compile
// const volatile int& cvr = 0;
// This does compile
const int& cr = 0;
const volatile int& cvr = cr;
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