2012-06-26 8 views
55

C++ 11 supporta una nuova funzione sintassi:Lo stile di sintassi di tipo trailing return dovrebbe diventare l'impostazione predefinita per i nuovi programmi C++ 11?

auto func_name(int x, int y) -> int; 

Attualmente questa funzione sarebbe stata dichiarata come:

int func_name(int x, int y); 

Il nuovo stile non sembra essere ampiamente ancora adottato (diciamo nel GCC stl)

Tuttavia, questo nuovo stile dovrebbe essere preferito ovunque nei nuovi programmi C++ 11 o verrà utilizzato solo quando necessario?

Personalmente, preferisco il vecchio stile quando possibile, ma un codice con stili misti sembra piuttosto brutto.

+23

è lì principalmente per 'decltype' su argomenti. –

+0

cosa dice CatPlusPlus: non ha molto senso utilizzarlo nell'esempio – stijn

+0

@Cat Plus Plus Ciò significa che si lasciano le cose come sono in C++ 03, a meno che non sia necessario ricavare il tipo restituito? – mirk

risposta

71

In alcuni casi è necessario utilizzare un tipo di ritorno finale. In particolare, un tipo di ritorno lambda, se specificato, deve essere specificato tramite un tipo di ritorno finale. Inoltre, se il tuo tipo di ritorno utilizza uno decltype che richiede che i nomi degli argomenti siano in ambito, deve essere utilizzato un tipo di ritorno finale (tuttavia, è possibile utilizzare in genere declval<T> per aggirare quest'ultimo problema).

Il tipo di ritorno finale presenta altri vantaggi minori. Ad esempio, si consideri una definizione di funzione membro non in linea con il tradizionale sintassi della funzione:

struct my_awesome_type 
{ 
    typedef std::vector<int> integer_sequence; 

    integer_sequence get_integers() const; 
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const 
{ 
    // ... 
} 

typedef membri non sono di portata fino a dopo il nome della classe appare prima ::get_integers, quindi dobbiamo ripetere la qualifica di classe due volte . Se si usa un tipo di ritorno finale, non abbiamo bisogno di ripetere il nome del tipo:

auto my_awesome_type::get_integers() const -> integer_sequence 
{ 
    // ... 
} 

In questo esempio, non è un grosso problema, ma se si dispone di nomi lunghi di classe o funzioni membro della i modelli di classe che non sono definiti in linea, possono fare una grande differenza nella leggibilità.

Nel suo "Fresh Paint" sessione in C++ Ora 2012, Alisdair Meredith ha sottolineato che se si utilizza finali tipi restituiti in modo coerente, i nomi di tutte le funzioni in fila ordinatamente:

auto foo() -> int; 
auto bar() -> really_long_typedef_name; 

ho usato i tipi di ritorno finali dappertutto in CxxReflect, quindi se stai cercando un esempio di come il codice sembra utilizzarli in modo coerente, puoi dare un'occhiata lì (ad esempio, the type class).

+1

Non sembra che esista ancora un consenso, ma è interessante guardare a CxxReflect con il nuovo stile. – mirk

+0

Ciao James. Questa risposta potrebbe probabilmente essere resa più accurata alla luce dello standard C++ 14. –

6

Vedi questo simpatico articolo: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html ottimo esempio quando utilizzare questa sintassi senza decltype in gioco:

class Person 
{ 
public: 
    enum PersonType { ADULT, CHILD, SENIOR }; 
    void setPersonType (PersonType person_type); 
    PersonType getPersonType(); 
private: 
    PersonType _person_type; 
}; 

auto Person::getPersonType() -> PersonType 
{ 
    return _person_type; 
} 

e la spiegazione brillante rubato anche da un articolo di Alex Allain "Perché il valore di ritorno va alla fine del la funzione, invece di prima, non è necessario aggiungere l'ambito della classe."

confronta con questo possibile caso in cui uno per caso dimenticare ambito di classe, e, per i più grandi disastri, un'altra PersonType è definito in ambito globale:

typedef float PersonType; // just for even more trouble 
/*missing: Person::*/ 
PersonType Person::getPersonType() 
{ 
    return _person_type; 
} 
+5

Non sono sicuro che questo rientri nella categoria "disastro": se il tipo è sbagliato, il codice non verrà compilato. Gli errori di runtime possono avere conseguenze disastrose; errori in fase di compilazione, non così tanto. –

+2

@JamesMcNellis confronta l'output del compilatore: 'prog.cpp: 13: 12: errore: prototipo per 'PersonType Person :: getPersonType()' non corrisponde a nessuno nella classe 'Person' vs. ' prog.cpp: 13: 1: errore: "PersonType" non indica un tipo " Il primo errore del compilatore è, almeno per me, peggio da capire. – PiotrNycz

46

Oltre a ciò che gli altri dicono, il ritorno finale digitare permette anche di utilizzare this, che non è altrimenti consentito

struct A { 
    std::vector<int> a; 

    // OK, works as expected 
    auto begin() const -> decltype(a.begin()) { return a.begin(); } 

    // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
    // the return statement returns "const_iterator" 
    decltype(a.end()) end() const { return a.end(); } 
}; 

Nella seconda dichiarazione, abbiamo usato lo stile tradizionale. Tuttavia, poiché this non è consentito in quella posizione, il compilatore d o non lo usa implicitamente. Quindi lo a.end() utilizza il tipo staticamente dichiarato di a per determinare quale sovraccarico di end di vector<int> chiamerà, che risulta essere la versione non costante.

7

Un altro vantaggio è che la sintassi del tipo di ritorno trailing può essere più leggibile quando la funzione restituisce un puntatore a una funzione. Ad esempio, confrontare

void (*get_func_on(int i))(int); 

con

auto get_func_on(int i) -> void (*)(int); 

Tuttavia, si può sostenere che meglio leggibilità può essere ottenuto semplicemente introducendo un tipo alias per il puntatore funzione:

using FuncPtr = void (*)(int); 
FuncPtr get_func_on(int i); 
Problemi correlati