Le style de syntaxe du type de retour de fin doit-il devenir le style par défaut pour les nouveaux programmes C ++ 11? [fermé]


92

C ++ 11 prend en charge une nouvelle syntaxe de fonction:

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

Actuellement, cette fonction serait déclarée comme:

int func_name(int x, int y);

Le nouveau style ne semble pas encore largement adopté (disons dans le gcc stl)

Cependant, ce nouveau style devrait-il être préféré partout dans les nouveaux programmes C ++ 11, ou ne sera-t-il utilisé qu'en cas de besoin?

Personnellement, je préfère l'ancien style lorsque c'est possible, mais une base de code avec des styles mixtes semble assez moche.


29
C'est là surtout pour decltypeles arguments.
Cat Plus Plus

ce que dit CatPlusPlus: n'a pas beaucoup de sens de l'utiliser dans votre exemple
stijn

@Cat Plus Plus Cela signifie que vous laissez les choses telles qu'elles sont en C ++ 03, sauf si vous devez dériver le type de retour?
mirk

1
Moche d'avoir à spécifier "auto" devant chaque fonction. Est-ce que c'est comme la réponse racée de C ++ au "def" de python?
Erik Aronesty

Réponses:


110

Il existe certains cas où vous devez utiliser un type de retour de fin. Plus particulièrement, un type de retour lambda, s'il est spécifié, doit être spécifié via un type de retour de fin. En outre, si votre type de retour utilise un decltypequi nécessite que les noms d'argument soient dans la portée, un type de retour de fin doit être utilisé (cependant, on peut généralement l'utiliser declval<T>pour contourner ce dernier problème).

Le type de retour de fin présente d'autres avantages mineurs. Par exemple, considérons une définition de fonction membre non en ligne utilisant la syntaxe de fonction traditionnelle:

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

Les typedefs de membres ne sont dans la portée qu'après l'apparition du nom de la classe ::get_integers, nous devons donc répéter la qualification de classe deux fois. Si nous utilisons un type de retour de fin, nous n'avons pas besoin de répéter le nom du type:

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

Dans cet exemple, ce n'est pas si grave, mais si vous avez des noms de classe longs ou des fonctions membres de modèles de classe qui ne sont pas définis en ligne, cela peut faire une grande différence en termes de lisibilité.

Dans sa session "Fresh Paint" à C ++ Now 2012, Alisdair Meredith a souligné que si vous utilisez les types de retour de fin de manière cohérente, les noms de toutes vos fonctions s'alignent parfaitement:

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

J'ai utilisé des types de retour de fin partout dans CxxReflect , donc si vous cherchez un exemple de l'apparence du code en les utilisant de manière cohérente, vous pouvez y jeter un œil (par exemple, la typeclasse ).


1
Il ne semble pas qu'il y ait encore de consensus, mais il est intéressant de regarder CxxReflect avec le nouveau style.
mirk

Bonjour James. Cette réponse pourrait probablement être rendue plus précise à la lumière de la norme C ++ 14.
Drew Dormann

@DrewDormann Que voulez-vous ajouter / changer?
underscore_d

L'alignement est en fait un gros plus, au point que j'aurais souhaité qu'il y ait un nouveau mot-clé «func» pour remplacer le «auto» sans signification ici.
Johan Boulé

67

En plus de ce que d'autres ont dit, le type de retour de fin permet également d'utiliser this, ce qui n'est pas autorisé autrement

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

Dans la deuxième déclaration, nous avons utilisé le style traditionnel. Cependant, puisque ce thisn'est pas autorisé à cette position, le compilateur ne l'utilise pas implicitement. Donc, le a.end()utilise le type déclaré statiquement de apour déterminer quelle endsurcharge vector<int>va appeler, ce qui finit par être la version non const.


2
Bien que ce soit une bonne démonstration du concept (en utilisant des membres dans des types de retour), c'est amusant car en C ++ 14, la spécification d'un type est totalement redondante dans une définition en ligne sans conversion; nous pouvons maintenant simplement utiliser la déduction de type de retour complet. : P
underscore_d

27

Un autre avantage est que la syntaxe de type retour de fin peut être plus lisible lorsque la fonction renvoie un pointeur vers une fonction. Par exemple, comparez

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

avec

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

Cependant, on peut affirmer qu'une meilleure lisibilité peut être obtenue simplement en introduisant un alias de type pour le pointeur de fonction:

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

10

Voir cet article sympa: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Très bon exemple quand utiliser cette syntaxe sans decltype dans le jeu :

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

Et une explication brillante également volée à l'article d'Alex Allain "Parce que la valeur de retour va à la fin de la fonction, au lieu d'être avant, vous n'avez pas besoin d'ajouter la portée de la classe."

Comparez à ce cas possible quand on oublie accidentellement la portée de la classe et, pour un désastre plus important, un autre PersonType est défini dans la portée globale:

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

7
Je ne suis pas sûr que cela tombe dans la catégorie "catastrophe": si le type est incorrect, le code ne se compilera pas. Les erreurs d'exécution peuvent avoir des conséquences désastreuses; erreurs de compilation, pas tellement.
James McNellis

4
@JamesMcNellis compare la sortie du compilateur: 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 La première erreur du compilateur est, du moins pour moi, pire à comprendre.
PiotrNycz

Personnellement, je ne suis pas d'accord, je trouve le deuxième message plus difficile à lire, et je préfère que l'implémentation ressemble à la déclaration.
jrh
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.