Chciałbym zrozumieć, dlaczego stałe zmienne odniesienie nie może być powiązane z odniesieniem wartości? Jaki jest racjonalny powód, aby zakazać takiej konwersji?
W poniższym kodzie komentuję wiersze, które się nie kompilują:
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?
}
Oto bardziej realistyczny scenariusz, którego nie można skompilować z podobnego powodu:
#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 &
}
W tym kodzie spodziewałbym się tego g<volatile int>(const volatile&)
przyjąć dowolny argument.
Kolejna edycja, na bardziej konkretny przykład:
#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());
//...
//...
}
Musi istnieć dobry powód tego ograniczenia, nie?
Odpowiedzi:
7 dla odpowiedzi № 1Prawidłowe pytanie powinno brzmieć „Dlaczego stałe zmienne odniesienie nie może być powiązane z wartością?? "
Poniższy kod również nie jest kompilowany, chociaż nie są bezpośrednio związane żadne odwołania do wartości:
const volatile int& cvr = 0;
Ta odpowiedź na powiązane pytanie przytacza odpowiednią sekcję normy:
Za [dcl.init.ref]/ 5, aby referencja była inicjowana przez powiązanie z wartością, referencja musi być a
const
nie-volatile
odwołanie do wartości lub odwołanie do wartości:- W przeciwnym razie odniesienie będzie odniesieniem wartościowym do trwałego typu stałego (tj. CV1 niech będzie
const
) lub odniesienie jest odniesieniem do wartości.
Domyślam się, że to ograniczenie ma charakter historycznywywodzi się ze standardu C ++ 98, gdzie wartości były ograniczone do tymczasowych, w pełni zarządzanych przez kompilator. Kompilator może umieścić wartość tymczasową pod dowolnym wybranym adresem lub rejestrem, a traktowanie go jako obiektu lotnego z możliwymi do zaobserwowania odczytami nie ma sensu. W nowym standardzie odwołanie do wartości można przekształcić w odwołanie do wartości za pomocą std::move()
, jednak w rezultacie otrzymuje stare właściwości przyjęte dla wartości, tj. że ich dokładny adres pamięci jest nieznaczny, a zatem nie można mu przypisać atrybutu niestabilności.
Technicznie ograniczenie to nie jest bardzo ograniczające, ponieważ można skutecznie powiązać const volatile
odniesienie do wartości poprzez dodatkowy poziom pośredni:
// This doesn"t compile
// const volatile int& cvr = 0;
// This does compile
const int& cr = 0;
const volatile int& cvr = cr;
4 dla odpowiedzi nr 2
Dosłowny powód jest taki, że [dcl.init.ref] zabrania takiej deklaracji:
Odwołanie do typu „cv1 T1” jest inicjalizowane przez wyrażenie typu „cv2 T2” w następujący sposób:
- Jeśli odwołanie jest odwołaniem do wartości i wyrażeniem inicjującym [...]
- W przeciwnym razie odniesienie powinno być odniesieniem wartości do trwałego typu stałego (tj. cv1 będzie const), lub referencja będzie referencją wartości.
Powód następnego poziomu to tylko zgadywanie (i problem z zadawaniem pytań): taka deklaracja nie ma sensu. Cel volatile
jest wykonywanie wszystkich odczytów i zapisów możliwych do zaobserwowania zachowań. Jeśli inicjujesz odwołanie do const volatile
tymczasowo, jesteś teraz właścicielem życia tego obiektu. Ale nie możesz do niego pisać. I nikt też nie może. Co więc robi volatile
przenieść?