2015-05-20 14 views
7

Durante le mie avventure con il progetto mi sono reso conto che non posso usare il vantaggio della nuova parola chiave auto C++ 11 se voglio inizializzare il parametro a seconda della condizione.C++ auto con più scelte

Fondamentalmente ho avuto un frammento di codice come questo:

auto foo = bar::getfoo(); 

che doveva essere il cambiamento a:

FOO foo 
if(cond){ 
    foo = bar::getfoo(); 
} else { 
    foo = baz::getotherfoo(); 
} 

Ma poi ho bisogno di dichiarare la foo con il tipo (come il compilatore può So che userò lo stesso tipo di ritorno Mi stavo chiedendo se c'è un modo di usare la parola chiave auto in questo caso .. L'altra soluzione che ho trovato utilizza l'operatore?: con tale codice:

auto foo = cond ? bar::getfoo() : baz::getotherfoo(); 

Ma se ci sono più di due funzioni tra cui scegliere non desidero concatenare il? : operatori. Mi chiedo se ci sia qualche schema valido da utilizzare in questo caso.

+0

È un peccato che tu sia limitato a C++ 11. In C++ 14, utilizzare una funzione o lambda con un tipo di ritorno dedotto. –

+0

@ mike quasi ogni compilatore C++ 11 ha supportato la deduzione avanzata del tipo restituito su lambdas praticamente fuori dal gate. – Yakk

+0

La domanda è formulata in modo un po 'errato. Non ci sono scelte multiple. La scelta è una, perché tutti i "percorsi" valutano lo stesso tipo. Tuttavia OP vuole tipo "posticiparla" dopo la dichiarazione di dichiarazione delle variabili. Per scelte multiple sono necessari 'boost :: any' o qualcosa del genere. – luk32

risposta

7

tuo istinto per utilizzare la? : gli operatori sono corretti - è una buona idea inizializzare una variabile solo una volta ed evitare il ciclo ridondante di costruzione/assegnazione predefinito.

Un modo per farlo è di rinviare la selezione della creazione Foo ad una piccola funzione di utilità:

auto make_foo() -> decltype(bar::getfoo()) 
{ 
    if (condition1()) { 
     return bar::getfoo(); 
    } 
    else if(condition2()) { 
     return baz::getfoo(); 
    } 
    else { 
     return banana::getfoo(); 
    } 
} 

void foo_test() { 

    auto foo = make_foo(); 
} 

Nota che a causa di Return Value Optimization, questo è estremamente efficiente.

Se si dispone di C++ 14 è ancora più bello - il make_foo() può dedurre il proprio tipo di ritorno:

auto make_foo() { 
    if (condition1()) { 
     return bar::getfoo(); 
    } 
    else if(condition2()) { 
     return baz::getfoo(); 
    } 
    else { 
     return banana::getfoo(); 
    } 
} 
+0

questo è tutto a posto ma devi definire il tipo di ritorno di make_foo, quindi sta sconfiggendo il punto di fare tutto il lavoro perché puoi nominare il scrivi comunque. L'idea è di disaccoppiare il tipo di ritorno di bar :: getfoo() e l'origine del file corrente (quindi quando lo cambi non è necessario cambiare l'altro codice sorgente del file, che non è il caso qui il caso di C++ 14 lo risolve ma è C++ 14 che non posso usare) – cerkiewny

+1

@cerkiewny ok questo può essere fatto con un piccolo edit, sec ... fatto –

+0

Anche una riga: 'return condition1()? bar :: getfoo(): condition2()? baz :: getfoo(): banana :: getfoo(); ' – edmz

5

Questo è ciò che è decltype. Esso vi fornirà il tipo di espressione, senza in realtà la valutazione (e si potrebbe implementare in termini di auto off):

decltype(bar::getfoo()) foo; 
if (....) {foo = ...;} else {foo = ...;} 
+0

questo approccio non funzionerà per i tipi const a causa del compito che modifica il loro contenuto. – cerkiewny

+0

@cerkiewny Questo è un problema non correlato che si applicherebbe anche nel caso in cui il tipo sia effettivamente noto in anticipo e nel qual caso probabilmente sarebbe il momento di usare un ternario o se ci sono molte possibilità per un lamda. Potresti usare il primo esempio di Yakks, ma sostituire il costrutto if else. – Lanting

+0

@Lanting ciò a cui cerkiewny si riferisce è che decltype() può risolversi, diciamo "const int &" (se questo è il valore di ritorno) mentre auto si risolverebbe solo in "int", definendo una copia mutabile (che direi è il comportamento corretto in questo caso). Puoi risolvere questo problema usando i tratti remove_const e remove_reference, ma diventa rapidamente ingombrante – Arvid

4
auto a = foo(); 

costrutti a da foo();

std::decay<decltype(foo())>::type a; 
if(cond){ 
    a = foo(); 
}else{ 
    a = bar(); 
} 

predefinita costruisce a poi assegna ad essa il risultato di foo() o bar() sulla base di cond.

decay è necessario in quanto in caso contrario di foo() restituisce un riferimento o un const decltype sarà dedurre il tipo di 'sbagliato' per a.

auto a = [&]{ 
    if (cond) 
    return foo(); 
    else 
    return bar(); 
}(); 

utilizza una funzione C++ 14 (che la maggior parte C++ 11 compilatori supportati nella fase iniziale), ma condizionatamente chiamate sia foo o bar e costruisce a da esso. Questo logico richiede una mossa in più, ma il compilatore quasi sicuramente lo eliderà, quindi non accadrà.

+0

Si prega di chiarire quale C++ 14 presenta le esigenze di esempio in basso. Non ho visto un compilatore che supporta lambda e * non * lo supporta. – lefticus

+0

@lefticus ha bisogno di una deduzione avanzata per il tipo di ritorno per lambda. Sotto C++ 11, i lambda che consistono in una dichiarazione possono essere dedotti tipo di ritorno. Sotto C++ 14, la deduzione del tipo di ritorno lambda che è richiesto * ogni * C++ 11 compilatore che abbia mai provato (incluso il precedente), che tra le altre cose consente più istruzioni. Lo stile C++ 14 era (da quello che posso ricordare) sempre inteso, ma il C++ 11 "più semplice" è stato inserito (per rendere più semplice per gli implementatori di compilatori forse). Apparentemente lo stile C++ 14 era abbastanza facile. – Yakk