Sollte der Syntaxstil des nachfolgenden Rückgabetyps zum Standard für neue C ++ 11-Programme werden? [geschlossen]


88

C ++ 11 unterstützt eine neue Funktionssyntax:

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

Derzeit wird diese Funktion wie folgt deklariert:

int func_name(int x, int y);

Der neue Stil scheint noch nicht weit verbreitet zu sein (etwa in der gcc stl)

Sollte dieser neue Stil jedoch überall in neuen C ++ 11-Programmen bevorzugt werden, oder wird er nur bei Bedarf verwendet?

Persönlich bevorzuge ich den alten Stil, wenn möglich, aber eine Codebasis mit gemischten Stilen sieht ziemlich hässlich aus.


29
Es ist hauptsächlich für decltypeArgumente da.
Cat Plus Plus

Was CatPlusPlus sagt: Es macht nicht viel Sinn, es in Ihrem Beispiel zu verwenden
stijn

@Cat Plus Plus Dies bedeutet, dass Sie die Dinge so lassen, wie sie in C ++ 03 sind, es sei denn, Sie müssen den Rückgabetyp ableiten?
Mirk

1
Hässlich, vor jeder Funktion "auto" angeben zu müssen. Ist das wie C ++ 'rassige Antwort auf Pythons "def"?
Erik Aronesty

Antworten:


106

In bestimmten Fällen müssen Sie einen nachgestellten Rückgabetyp verwenden. Insbesondere muss ein Lambda-Rückgabetyp, falls angegeben, über einen nachfolgenden Rückgabetyp angegeben werden. Wenn Ihr Rückgabetyp ein verwendet decltype, für das die Argumentnamen im Gültigkeitsbereich liegen müssen, muss ein nachfolgender Rückgabetyp verwendet werden (normalerweise kann man declval<T>dieses letztere Problem jedoch umgehen).

Der nachfolgende Rückgabetyp hat einige andere geringfügige Vorteile. Betrachten Sie beispielsweise eine Nicht-Inline-Elementfunktionsdefinition unter Verwendung der traditionellen Funktionssyntax:

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

Die Typedefs der Mitglieder sind erst im Geltungsbereich, nachdem der Name der Klasse zuvor angezeigt wurde. Daher ::get_integersmüssen wir die Klassenqualifikation zweimal wiederholen. Wenn wir einen nachgestellten Rückgabetyp verwenden, müssen wir den Namen des Typs nicht wiederholen:

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

In diesem Beispiel ist das keine große Sache, aber wenn Sie lange Klassennamen oder Elementfunktionen von Klassenvorlagen haben, die nicht inline definiert sind, kann dies einen großen Unterschied in der Lesbarkeit bewirken.

In seiner "Fresh Paint" -Sitzung bei C ++ Now 2012 wies Alisdair Meredith darauf hin, dass die Namen aller Ihrer Funktionen genau übereinstimmen, wenn Sie nachfolgende Rückgabetypen konsistent verwenden:

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

Ich habe überall in CxxReflect nachgestellte Rückgabetypen verwendet. Wenn Sie also nach einem Beispiel suchen, wie Code mit ihnen konsistent aussieht, können Sie dort einen Blick darauf werfen (z. B. die typeKlasse ).


1
Es sieht noch nicht nach einem Konsens aus, aber es ist interessant, CxxReflect mit dem neuen Stil zu betrachten.
Mirk

Hallo James. Diese Antwort könnte angesichts des C ++ 14-Standards wahrscheinlich genauer gemacht werden.
Drew Dormann

@DrewDormann Was würden Sie hinzufügen / ändern?
underscore_d

Die Ausrichtung ist eigentlich ein großes Plus, bis zu dem Punkt, an dem ich mir wünschte, es gäbe ein neues 'func'-Schlüsselwort, um das bedeutungslose' auto 'hier zu ersetzen.
Johan Boulé

#define fn autound du kannst Code schreiben, der aussieht wie Rust ;-)
Michał Fita

66

Zusätzlich zu den Aussagen anderer erlaubt der nachfolgende Rückgabetyp auch die Verwendung this, was ansonsten nicht zulässig ist

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

In der zweiten Erklärung haben wir den traditionellen Stil verwendet. Da dies thisan dieser Position jedoch nicht zulässig ist, verwendet der Compiler es nicht implizit. Der a.end()verwendet also den statisch deklarierten Typ von a, um zu bestimmen, welche endÜberladung vector<int>davon aufgerufen wird, was letztendlich die Nicht-Konstanten-Version ist.


2
Dies ist zwar eine gute / übersichtliche Demonstration des Konzepts (Verwendung von Elementen in Rückgabetypen), aber es ist lustig, da in C ++ 14 die Angabe eines Typs in einer Inline-Definition ohne Konvertierung völlig redundant ist. Wir können jetzt nur den vollständigen Rückgabetypabzug verwenden. : P
underscore_d

25

Ein weiterer Vorteil besteht darin, dass die Syntax vom Typ Trailing-Return besser lesbar ist, wenn die Funktion einen Zeiger auf eine Funktion zurückgibt. Zum Beispiel vergleichen

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

mit

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

Man kann jedoch argumentieren, dass eine bessere Lesbarkeit einfach durch die Einführung eines Typalias für den Funktionszeiger erreicht werden kann:

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

10

Siehe diesen schönen Artikel: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Sehr gutes Beispiel für die Verwendung dieser Syntax ohne Dekltyp im Spiel ::

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

Und eine brillante Erklärung, die auch aus Alex Allains Artikel gestohlen wurde: "Da der Rückgabewert am Ende der Funktion steht und nicht davor, müssen Sie den Klassenbereich nicht hinzufügen."

Vergleichen Sie diesen möglichen Fall, wenn einer versehentlich den Klassenbereich vergisst und für größere Katastrophen ein anderer PersonType im globalen Bereich definiert ist:

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

7
Ich bin nicht sicher, ob dies in die Kategorie "Katastrophe" fällt: Wenn der Typ falsch ist, wird der Code nicht kompiliert. Laufzeitfehler können katastrophale Folgen haben. Fehler bei der Kompilierung, nicht so sehr.
James McNellis

4
@JamesMcNellis vergleicht die Compiler-Ausgabe: 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 Der erste Fehler des Compilers ist zumindest für mich schlechter zu verstehen.
PiotrNycz

Persönlich stimme ich nicht zu, ich finde die zweite Nachricht schwerer zu lesen, und ich möchte lieber, dass die Implementierung wie die Erklärung aussieht.
jrh
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.