Czy styl składni końcowego typu zwracanego powinien stać się domyślnym dla nowych programów w języku C ++ 11? [Zamknięte]


92

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.


29
Jest tam głównie do decltypekłótni.
Cat Plus Plus

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

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

1
Brzydkie jest określenie „auto” przed każdą funkcją. Czy to jak pikantna odpowiedź C ++ na „def” języka Python?
Erik Aronesty

Odpowiedzi:


110

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, decltypektó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 . typeKlasa ).


1
Wygląda na to, że nie ma jeszcze konsensusu, ale warto przyjrzeć się CxxReflect w nowym stylu.
mirk

Cześć James. Ta odpowiedź prawdopodobnie mogłaby być dokładniejsza w świetle standardu C ++ 14.
Drew Dormann

@DrewDormann Co byś dodał / zmienił?
underscore_d

Wyrównanie jest w rzeczywistości dużym plusem, do tego stopnia, że ​​chciałem, aby było nowe słowo kluczowe „func”, które zastąpiłoby tutaj bezsensowne „auto”.
Johan Boulé,

67

Oprócz tego, co powiedzieli inni, końcowy typ powrotu pozwala również na użycie this, co w innym przypadku nie jest dozwolone

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 zastosowaliśmy styl tradycyjny. Jednak ponieważ thisnie jest to dozwolone w tej pozycji, kompilator nie używa go w sposób niejawny. Tak więc a.end()używa statycznie zadeklarowanego typu of, aaby określić, jakie endprzeciążenie vector<int>będzie wywoływane, co kończy się na wersji niestałej.


2
Chociaż jest to dobra / zgrabna demonstracja koncepcji (używanie elementów składowych w typach zwracanych), jest to zabawne, ponieważ w C ++ 14 określanie typu jest całkowicie zbędne w definicji wbudowanej bez konwersji; możemy teraz po prostu użyć odliczenia pełnego zwrotu typu. : P
underscore_d

27

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

10

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

7
Nie jestem pewien, czy należy to do kategorii „katastrofa”: jeśli typ jest nieprawidłowy, kod nie zostanie skompilowany. Błędy w czasie wykonywania mogą mieć katastrofalne konsekwencje; błędy czasu kompilacji, nie tak bardzo.
James McNellis

4
@JamesMcNellis porównuje dane wyjściowe kompilatora: 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.
PiotrNycz

Osobiście nie zgadzam się, druga wiadomość jest trudniejsza do odczytania i wolałbym, żeby implementacja wyglądała jak deklaracja.
jrh
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.