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))); }
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))); ^
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 aT2
” se un’espressione di tipo “puntatore aT1
” può essere convertita esplicitamente nel tipo “puntatore aT2
” utilizzando unreinterpret_cast
. Il risultato si riferisce allo stesso object del glVue di origine, ma con il tipo specificato. [Nota: Cioè, per lvalues, un cast di riferimentoreinterpret_cast
ha lo stesso effetto della conversione(x) *reinterpret_cast
con gli operatori(&x) &
e*
(e allo stesso modo perreinterpret_cast
). – 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(x)
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 aT2
” se un’espressione di tipo “puntatore aT1
” può essere convertita esplicitamente nel tipo “puntatore aT2
” utilizzando unreinterpret_cast
. Il risultato si riferisce allo stesso object del glVue di origine, ma con il tipo specificato. [Nota: Cioè, per lvalues, un cast di riferimentoreinterpret_cast
ha lo stesso effetto della conversione(x) *reinterpret_cast
con gli operatori(&x) &
e*
(e allo stesso modo perreinterpret_cast
. -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).(x))
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.