Lo stile di syntax di tipo trailing return dovrebbe diventare l’impostazione predefinita per i nuovi programmi C ++ 11?

C ++ 11 supporta una nuova syntax della funzione:

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

Attualmente questa funzione verrebbe dichiarata come:

 int func_name(int x, int y); 

Il nuovo stile non sembra essere ancora ampiamente adottato (per esempio 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 ansible, ma un codice con stili misti sembra piuttosto brutto.

Vi sono alcuni casi in cui è 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 un decltype che richiede che i nomi degli argomenti siano in ambito, deve essere utilizzato un tipo di ritorno finale (tuttavia, solitamente è ansible utilizzare declval per aggirare quest’ultimo problema).

Il tipo di ritorno finale ha altri vantaggi minori. Ad esempio, considera una definizione di funzione membro non in linea utilizzando la syntax della funzione tradizionale:

 struct my_awesome_type { typedef std::vector integer_sequence; integer_sequence get_integers() const; }; my_awesome_type::integer_sequence my_awesome_type::get_integers() const { // ... } 

I typedef dei membri non sono ::get_integers prima che il nome della class appaia prima di ::get_integers , quindi dobbiamo ripetere la qualifica della class due volte. Se utilizziamo un tipo di ritorno finale, non è necessario ripetere il nome del tipo:

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

In questo esempio, non è un grosso problema, ma se hai nomi di classi lunghi o funzioni membro di modelli di class che non sono definiti in linea, allora può fare una grande differenza nella leggibilità.

Nella sua sessione “Fresh Paint” al C ++ Now 2012, Alisdair Meredith ha sottolineato che se si utilizzano costantemente tipi di ritorno finali, i nomi di tutte le funzioni si allineano perfettamente:

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

Ho utilizzato i tipi di ritorno finali ovunque in CxxReflect , quindi se stai cercando un esempio di come il codice li usi in modo coerente, puoi dare un’occhiata lì (ad esempio, la class del type ).

Oltre a ciò che altri hanno detto, il tipo di ritorno finale consente anche di utilizzare this , che non è altrimenti consentito

 struct A { std::vector 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 non lo usa implicitamente. Quindi a.end() usa il tipo staticamente dichiarato di a per determinare quale sovraccarico di end del vector chiamerà, che finisce per essere la versione non-const.

Un altro vantaggio è che la syntax trailing-return-type può essere più leggibile quando la funzione restituisce un puntatore a una funzione. Ad esempio, confronta

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

con

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

Tuttavia, si può sostenere che una migliore leggibilità può essere raggiunta semplicemente introducendo un alias di tipo per il puntatore alla funzione:

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

Vedi questo bell’articolo: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Ottimo esempio quando usare questa syntax 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 una spiegazione brillante è stata rubata anche dall’articolo di Alex Allain “Perché il valore restituito va alla fine della funzione, invece di prima, non è necessario aggiungere l’ambito della class.”

Paragonare a questo caso ansible quando uno per errore dimentica l’ambito della class e, per un disastro più grande, un altro PersonType è definito nell’ambito globale:

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