2012-06-26 7 views
55

C++ 11 obsługuje nową funkcję składni:Czy końcowy styl składni powrotu powinien być domyślny dla nowych programów w C++ 11?

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

Obecnie ta funkcja zostanie zadeklarowana jako:

int func_name(int x, int y); 

Nowy styl nie wydaje się być jeszcze powszechnie przyjęte (powiedzmy w gcc stl)

Jednak, czy ten nowy styl powinien być preferowany wszędzie w nowych programach C++ 11, czy też będzie używany tylko w razie potrzeby?

Osobiście preferuję stary styl, jeśli to możliwe, ale baza kodowa z mieszanym stylem wygląda dość brzydko.

+23

To tam głównie dla 'decltype' na argumentach. –

+0

co mówi CatPlusPlus: nie ma sensu używać go w twoim przykładzie. – stijn

+0

@Cat Plus Plus Oznacza to, że zostawiasz rzeczy takie, jakie są w C++ 03, chyba że musisz wyprowadzić typ zwrotu? – mirk

Odpowiedz

71

Istnieją pewne przypadki, w których należy użyć końcowego typu zwrotu. Przede wszystkim, zwracany typ lambda, jeśli został określony, musi być określony przez końcowy typ powrotu. Ponadto, jeśli twój typ zwracany używa decltype, który wymaga, aby nazwy argumentów były w zakresie, musi być użyty końcowy typ zwracania (jednak zwykle można użyć declval<T> do obejścia tego ostatniego problemu).

Końcowy typ powrotu ma kilka innych niewielkich zalet. Na przykład, rozważmy non-inline definicję funkcji członek metodą tradycyjną funkcję składni:

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 
{ 
    // ... 
} 

typedefs państwa nie są w zakresie aż po nazwie klasy pojawia się przed ::get_integers, więc musimy powtarzać klasę kwalifikacje dwukrotnie . Jeśli używamy spływu typ zwracany, nie trzeba powtarzać nazwy typu:

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

W tym przykładzie, to nie jest taka wielka sprawa, ale jeśli masz długie nazwy klasy lub funkcji Członka szablony klas, które nie są zdefiniowane w linii, może to znacznie zmienić czytelność.

W swoim "Fresh Paint" sesji w C++ Teraz 2012 Alisdair Meredith zwrócił uwagę, że jeśli używasz spływu rodzaje powrotne konsekwentnie, imiona wszystkich swoich funkcji w kolejce porządnie:

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

Użyłem końcowe typy powrotu wszędzie w CxxReflect, więc jeśli szukasz przykładu, w jaki sposób kod wygląda spójnie, możesz tam zajrzeć (np. the type class).

+1

Wygląda na to, że nie ma consensusu, ale interesujące jest spojrzenie na CxxReflect z nowym stylem. – mirk

+0

Cześć, James. Ta odpowiedź mogłaby prawdopodobnie być bardziej dokładna w świetle standardu C++ 14. –

6

Zobacz ten piękny artykuł: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html bardzo dobry przykład, kiedy używać tej składni bez decltype w grze:

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 genialny wyjaśnienie również skradzione z art Alex Allain za „Ponieważ wartość zwracana idzie na koniec Funkcja, zamiast przed nią, nie trzeba dodawać zakresu klasy."

Porównaj z tym możliwym przypadku, gdy jeden przez przypadek zapomnieć o zasięgu klasy, a dla większej katastrofy, kolejna PersonType jest zdefiniowane w zakresie globalnym:

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

Nie jestem pewien, czy to należy do kategorii "katastrofy": jeśli typ jest nieprawidłowy, kod nie zostanie skompilowany. Błędy środowiska wykonawczego mogą mieć katastrofalne konsekwencje; błędy podczas kompilacji, nie tak bardzo. –

+2

@JamesMcNellis porównaj dane wyjściowe kompilatora: 'prog.cpp: 13: 12: error: prototype for 'PersonType Person :: getPersonType()' nie pasuje do żadnej z klasy 'Person'' vs. ' prog.cpp: 13: 1: błąd: "PersonType" nie określa typu " Pierwszy błąd kompilatora jest, przynajmniej dla mnie, gorszy do zrozumienia. – PiotrNycz

46

oprócz tego, co inni mówili, powrót spływu wpisać także pozwala na użycie this, który nie jest w inny sposób dozwolony

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(); } 
}; 

W drugiej deklaracji użyliśmy tradycyjny styl. jednak od this nie jest dozwolone w tej pozycji, kompilator d nie używają go bezwarunkowo. Tak więc a.end() używa statycznie zadeklarowanego typu a, aby ustalić, które z nich ma zostać wywołane, co kończy się niestanowiącą wersją.

7

Inną zaletą jest to, że składnia typu powrót-powrót może być bardziej czytelna, gdy funkcja zwraca wskaźnik do funkcji. Na przykład, porównanie

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

z

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

Można jednak argumentować, że lepsza czytelność można osiągnąć jedynie poprzez wprowadzenie typu alias dla wskaźnika funkcji:

using FuncPtr = void (*)(int); 
FuncPtr get_func_on(int i); 
Powiązane problemy