2016-04-21 21 views
17

Sto provando a trovare un semplice esempio per un'operazione che si traduce in un valore.Perché il risultato di "decltype (i + j)" non è un riferimento di rvalue?

Questo test case avrebbe dovuto funzionare, ma sorprendentemente (per me), il risultato dell'aggiunta di due int s non è un valore di riferimento (riferimento). Cosa mi manca qui?

void test(int i, int j) 
{ 
    // this assert should pass, but fails: 
    static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue"); 
    // this assert passed, but should fail: 
    static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail..."); 
} 
+0

È solo semplice vecchi dati. Non penso che tu possa std :: move an int .. – 0xbaadf00d

+0

Questo è sempre stato int, da tempo immemorabile (a.k.a. primo Unix). Come ti muoveresti '2 + 2'? – bipll

+5

Puoi molto 'std :: move' an' int' e, oltre al punto, l'OP sta chiedendo la categoria di valore di' i + j'. –

risposta

33

i + j è un prvalue expression,

A prvalue ("pura rvalue") espressione è un'espressione che non ha identità e può essere spostato da.

a + b, a b%, un & b, a < < b, e tutte le altre espressioni aritmetiche built-in;

non xvalue,

Un xValue ("scadenza value") espressione è un'espressione che ha identità e può essere spostato da.

E decltype specifier cede T per prvalue, non T&&.

a) se la categoria valore di espressione è xValue, poi cede decltype T & &;
b) se la categoria di valore di espressione è lvalue, decltype restituisce T &;
c) se la categoria del valore di espressione è prvalue, quindi decltype cede T.

Si può rendere xValue da std::move:

static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail"); 
+1

Il paragrafo su decltype è quello che mi è mancato nel caso più generale. (O, più in generale, la differenza tra le * categorie di valori * e i riferimenti * che associano a *.) Grazie! – anderas

+0

Ho chiarito un po 'la domanda ora che so cosa ho sbagliato. La differenza è fondamentalmente * è un rvalue * vs * si lega a un riferimento di rvalue *. Mi aspettavo di controllare il secondo, ma ho fatto qualcos'altro. Vorresti aggiungerlo alla tua risposta o dovrei scrivere la mia risposta auto-accettata? (Dal momento che sarebbe la soluzione del mio problema reale.) – anderas

+0

@anderas Penso che sia giusto scrivere la risposta, in base al proprio aspetto e comprensione. – songyuanyao

5

Sulla base di @Answer songyuanyao, ho notato che il mio errore Stavo controllando la cosa sbagliata: la mia intenzione era di verificare se il risultato di i+j fosse associare a un riferimento di rvalue, ma ho controllato se esso è un riferimento di rvalue.

decltype deduce il tipo basato sulla value category, non si basa su ciò che reference type il valore sarebbe legano a:

1) se la categoria del valore di espressione è xvalue, allora decltype rendimenti T&&;
2) se la categoria di valore di espressione è lvalue, quindi decltype produce T&;
3) se la categoria di valore di espressione è prvalue, quindi decltype produce T.

Come mostrato nella lista, dal C++ 11, rvalues non esistono come categoria distinta sul livello più basso. Ora sono una categoria composita che contiene sia prvalues sia xvalues. La domanda come scritta chiede se l'espressione è un rvalue reference e controlla se si tratta di un xvalue.

Dalla lista sopra, è chiaro che i+j è un prvalue, quindi si applica il terzo caso. Questo spiega perché decltype(i + j) è int e non int&&. Entrambi xvalues e prvaluessi legano a riferimenti di rvalue.

Così controllando se i+j si lega ad un lvalue reference o un rvalue reference conferma che, in effetti, lega a un rvalue reference:

void foo(const int& f) 
{ 
    std::cout << "binds to lvalue reference" << std::endl; 
} 

void foo(int&& f) 
{ 
    std::cout << "binds to rvalue reference" << std::endl; 
} 

void test(int i, int j) 
{ 
    foo(i); // lvalue -> lvalue ref 

    foo(std::move(i)); // xvalue -> rvalue ref 
    // (std::move converts the argument to a rvalue reference and returns it as an xvalue) 

    foo(i + j); // prvalue -> rvalue ref 
} 

In conclusione:i+j è non un riferimento rvalue , ma si lega a uno.

+0

Puoi fare un esempio che sia in grado di recuperare tutti e tre i tipi? – Chiel

+0

@Chiel, no, e questo è il punto che ho cercato di fare con la distinzione tra * categoria * e * ciò che un valore si lega a *: ci sono solo riferimenti 'rvalue' e' lvalue' (non 'xvalue', riferimenti 'lvalue' e' prvalue'). 'prvalues' AND' xvalues' si legano a riferimenti di valore. – anderas

+0

@ Chii a meno che tu non voglia che mostri a quali valori di queste categorie si legano. L'ho aggiunto al campione. – anderas

Problemi correlati