web-dev-qa-db-fra.com

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

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 être 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 cela est possible, mais une base de code avec des styles mixtes semble assez moche.

75
mirk

Dans certains cas, 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. De plus, si votre type de retour utilise un decltype qui 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 utiliser declval<T> pour contourner ce dernier problème).

Le type de retour arrière présente quelques autres avantages mineurs. Par exemple, considérez une définition de fonction de membre non en ligne en 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 des membres ne sont pas dans la portée tant que le nom de la classe n'apparaît pas avant ::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 dans la lisibilité.

Dans sa session "Fresh Paint" à C++ Now 2012, Alisdair Meredith a souligné que si vous utilisez systématiquement des types de retour de fin, 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 la façon dont le code les utilise de manière cohérente, vous pouvez y jeter un œil (par exemple, le type class ).

88
James McNellis

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 this n'est pas autorisé à cette position, le compilateur ne l'utilise pas implicitement. Ainsi, la a.end() utilise le type déclaré statiquement de a pour déterminer quelle end surcharge de vector<int> Elle va appeler, ce qui finit par être le non -version constante.

58

Un autre avantage est que la syntaxe de type retour de fin peut être plus lisible lorsque la fonction renvoie un pointeur sur 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);
18
s3rvac

Voir cet article de Nice: http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html Très bon exemple quand utilisez 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 brillante explication a également été volée dans l'article d'Alex Allain "Parce que la valeur de retour va à la fin de la fonction, au lieu d'avant, vous n'avez pas besoin d'ajouter l'étendue de la classe."

Comparez avec ce cas possible quand un accident oublie 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;
}
8
PiotrNycz