Reinterpret_cast (o qualsiasi cast) può convertire xvalues ​​in lvalue?

Il seguente codice è legale (da C ++ 11 e / o C ++ 14 standard (s))?

#include  #include  using namespace std; void foo(int &a) { cout << a << endl; } int main() { foo(reinterpret_cast(move(5))); } 
  • Se sì, è un comportamento indefinito?
  • Se non è un comportamento indefinito, posso persino mutare a dentro foo senza che diventi UB?

Si compila su clang 3.5, non su gcc 4.9. Errore GCC:

 ➤ g++-4.9 -std=c++1y sample.cpp -o sample sample.cpp: In function 'int main()': sample.cpp:11:40: error: invalid cast of an rvalue expression of type 'std::remove_reference::type {aka int}' to type 'int&' foo(reinterpret_cast(move(5))); ^ 

MODIFICARE

FYI, un cast personalizzato meno peloso del precedente e che funziona su C ++ 11 sia per GCC che per Clang, sarebbe la seguente funzione lvalue :

 #include  namespace non_std { template  constexpr T &lvalue(T &&r) noexcept { return r; } } void divs(int &a, int &b) { int t = a; a /= b; b /= t; } int main() { using namespace std; using namespace non_std; int i_care_for_this_one = 4; divs(i_care_for_this_one, lvalue(2)); cout << i_care_for_this_one << endl; } 

Aggiornamento: il codice è mal formato in C ++ 11. La risposta qui sotto è per C ++ 14. Vedi nota alla fine di questa risposta.

Credo che questo codice sia ben formato e ben definito . Ecco perché.

Il risultato di std::move è un xvalue [1], che è un tipo di glvalue; e la conversione di un glValue in un riferimento a lvalue con reinterpret_cast sembra essere consentita dal testo dello standard:

Un’espressione glvalue di tipo T1 può essere convertita nel tipo “riferimento a T2 ” se un’espressione di tipo “puntatore a T1 ” può essere convertita esplicitamente nel tipo “puntatore a T2 ” utilizzando un reinterpret_cast . Il risultato si riferisce allo stesso object del glVue di origine, ma con il tipo specificato. [Nota: Cioè, per lvalues, un cast di riferimento reinterpret_cast(x) ha lo stesso effetto della conversione *reinterpret_cast(&x) con gli operatori & e * (e allo stesso modo per reinterpret_cast(x) ). – nota finale] Nessun temporaneo viene creato, nessuna copia viene creata e i costruttori (12.1) o le funzioni di conversione (12.3) non vengono chiamati.73

Poiché “pointer to int ” può essere convertito in “pointer to int “, anche questo reinterpret_cast è consentito. Lo standard non dice nulla se il tipo di destinazione deve essere un riferimento di lvalue o un riferimento di rvalue.

Il risultato del cast è ben definito dal paragrafo precedente: si riferisce allo stesso object del glvalue sorgente — cioè, un object int temporaneo con il valore 5 . ([dcl.init.ref] specifica che viene creato un temporaneo quando un valore di predizione è associato a un riferimento.)

L’accesso al valore tramite int& inoltre non viola alcuna regola di aliasing poiché l’object originale era anche di tipo int . In effetti, credo che sarebbe persino ben definito modificare il temporaneo attraverso l’lvalue così ottenuto.

Nota: la dicitura C ++ 11 dice “espressione lvalue”, non “espressione glvalue”. La dicitura con “glvalue expression” proviene da N3936, che è il draft finale di lavoro per C ++ 14. Non sono un esperto di come funziona il processo di standardizzazione, ma credo che questo significhi che il cambio di “lvalue” in “glvalue” sia stato già votato dal comitato, e quando ISO pubblica lo standard C ++ 14, sta andando per essere abbastanza simile a quello che dice sopra.

[1] Tranne nel caso raro in cui l’argomento è una funzione; in tal caso il risultato è un lvalue, poiché non ci sono rvalori di funzione.

Il problema si verifica se reinterpret_cast è autorizzato a convertire xvalues ​​in lvalue. Contrariamente a ciò che altri stanno incollando, il paragrafo pertinente (5.2.10.11) menziona solo lvalue:

Un’espressione lvalue di tipo T1 può essere convertita nel tipo “riferimento a T2” se un’espressione di tipo “puntatore a T1” può essere convertita esplicitamente nel tipo “puntatore a T2” utilizzando un reinterpret_cast ..

C’è una proposta presentata da Michael Wong per cambiare il testo in glivalue, ma sembra che abbia colpito un muro:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1268

Suppongo che questo significhi, al momento, la conversione non sia legale, poiché consente solo esplicitamente la conversione da lvalue.

La sezione pertinente nello standard è la 5.2.10 [expr.reinterpret.cast]. Ci sono due paragrafi rilevanti:

Innanzitutto, c’è il paragrafo 1 che termina in:

Nessun’altra conversione può essere eseguita esplicitamente usando reinterpret_cast.

… e il paragrafo 11 come nessuno degli altri si applicano:

Un’espressione glvalue di tipo T1 può essere convertita nel tipo “riferimento a T2 ” se un’espressione di tipo “puntatore a T1 ” può essere convertita esplicitamente nel tipo “puntatore a T2 ” utilizzando un reinterpret_cast . Il risultato si riferisce allo stesso object del glVue di origine, ma con il tipo specificato. [Nota: Cioè, per lvalues, un cast di riferimento reinterpret_cast(x) ha lo stesso effetto della conversione *reinterpret_cast(&x) con gli operatori & e * (e allo stesso modo per reinterpret_cast(x)) . -End note] Nessun temporaneo viene creato, non viene eseguita alcuna copia e non vengono richiamati i costruttori (12.1) o le funzioni di conversione (12.3).

Tutte le altre clausole non si applicano agli oggetti ma solo a puntatori, puntatori a funzioni, ecc. Poiché un valore non è un glVue, il codice è illegale.