Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a const volatile reference cannot be bound to an rvalue reference?

Tags:

c++

c++11

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?

like image 739
Oliv Avatar asked Oct 22 '16 13:10

Oliv


1 Answers

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;
like image 137
Leon Avatar answered Oct 21 '22 22:10

Leon