C ++ 11 obsługuje nową składnię funkcji:
auto func_name(int x, int y) -> int;
Obecnie ta funkcja byłaby zadeklarowana jako:
int func_name(int x, int y);
Nowy styl nie wydaje się być jeszcze powszechnie przyjęty (powiedzmy w gcc stl)
Czy jednak ten nowy styl powinien być preferowany wszędzie w nowych programach w języku C ++ 11, czy też będzie używany tylko w razie potrzeby?
Osobiście wolę stary styl, jeśli to możliwe, ale baza kodu z mieszanymi stylami wygląda dość brzydko.
c++
c++11
auto
trailing-return-type
mroczny
źródło
źródło
decltype
kłótni.Odpowiedzi:
Istnieją pewne przypadki, w których należy użyć końcowego typu zwracanego. Przede wszystkim zwracany typ lambda, jeśli został określony, musi być określony za pośrednictwem końcowego typu zwracanego. Ponadto, jeśli typ zwracany wykorzystuje a,
decltype
który wymaga, aby nazwy argumentów znajdowały się w zakresie, należy użyć końcowego typu zwracanego (jednak zwykle można go użyćdeclval<T>
do obejścia tego ostatniego problemu).Końcowy zwracany typ ma inne drobne zalety. Rozważmy na przykład definicję funkcji składowej innej niż wbudowana, używając tradycyjnej składni funkcji:
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 { // ... }
Typy elementów członkowskich znajdują się w zakresie dopiero po pojawieniu się nazwy klasy przed
::get_integers
, więc musimy dwukrotnie powtórzyć kwalifikację klasy. Jeśli używamy końcowego typu zwracanego, nie musimy powtarzać nazwy typu:auto my_awesome_type::get_integers() const -> integer_sequence { // ... }
W tym przykładzie nie jest to taka wielka sprawa, ale jeśli masz długie nazwy klas lub funkcje składowe szablonów klas, które nie są zdefiniowane w tekście, może to mieć duży wpływ na czytelność.
W swojej sesji „Fresh Paint” w C ++ Now 2012 Alisdair Meredith zwrócił uwagę, że jeśli konsekwentnie używasz końcowych typów zwracanych, nazwy wszystkich funkcji są dobrze dopasowane:
auto foo() -> int; auto bar() -> really_long_typedef_name;
Użyłem końcowych typów zwracanych wszędzie w CxxReflect , więc jeśli szukasz przykładu, jak kod wygląda konsekwentnie przy ich użyciu, możesz tam zajrzeć (np .
type
Klasa ).źródło
Oprócz tego, co powiedzieli inni, końcowy typ powrotu pozwala również na użycie
this
, co w innym przypadku nie jest dozwolonestruct 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(); } };
W drugiej deklaracji zastosowaliśmy styl tradycyjny. Jednak ponieważ
this
nie jest to dozwolone w tej pozycji, kompilator nie używa go w sposób niejawny. Tak więca.end()
używa statycznie zadeklarowanego typu of,a
aby określić, jakieend
przeciążenievector<int>
będzie wywoływane, co kończy się na wersji niestałej.źródło
Inną zaletą jest to, że składnia typu końcowego-powrotu może być bardziej czytelna, gdy funkcja zwraca wskaźnik do funkcji. Na przykład porównaj
void (*get_func_on(int i))(int);
z
auto get_func_on(int i) -> void (*)(int);
Można jednak argumentować, że lepszą czytelność można osiągnąć po prostu wprowadzając alias typu dla wskaźnika funkcji:
using FuncPtr = void (*)(int); FuncPtr get_func_on(int i);
źródło
Zobacz ten fajny artykuł: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Bardzo dobry przykład, kiedy w grze używać tej składni bez decltype :
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; }
I genialne wyjaśnienie również skradzione z artykułu Alexa Allaina „Ponieważ wartość zwracana znajduje się na końcu funkcji, a nie przed nią, nie trzeba dodawać zakresu klas”.
Porównaj z tym możliwym przypadkiem, gdy przez przypadek zapomnisz o zakresie klasy, aw przypadku większej katastrofy inny typ PersonType jest zdefiniowany w zakresie globalnym:
typedef float PersonType; // just for even more trouble /*missing: Person::*/ PersonType Person::getPersonType () { return _person_type; }
źródło
prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'
vs.prog.cpp:13:1: error: 'PersonType' does not name a type
Pierwszy błąd kompilatora jest, przynajmniej dla mnie, gorzej do zrozumienia.