Avec la sortie de GCC 4.8.0, nous avons un compilateur qui prend en charge la déduction automatique du type de retour, qui fait partie de C ++ 14. Avec -std=c++1y
, je peux faire ceci:
auto foo() { //deduced to be int
return 5;
}
Ma question est la suivante: quand dois-je utiliser cette fonctionnalité? Quand est-ce nécessaire et quand rend-il le code plus propre?
Scénario 1
Le premier scénario auquel je peux penser est dans la mesure du possible. Chaque fonction qui peut être écrite de cette façon devrait l'être. Le problème avec cela est que cela ne rend pas toujours le code plus lisible.
Scénario 2
Le scénario suivant consiste à éviter des types de retour plus complexes. A titre d'exemple très léger:
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
Je ne crois pas que ce serait jamais vraiment un problème, bien que je suppose que le type de retour dépende explicitement des paramètres pourrait être plus clair dans certains cas.
Scénario 3
Ensuite, pour éviter la redondance:
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
En C ++ 11, nous pouvons parfois simplement return {5, 6, 7};
remplacer un vecteur, mais cela ne fonctionne pas toujours et nous devons spécifier le type à la fois dans l'en-tête de la fonction et dans le corps de la fonction. C'est purement redondant, et la déduction automatique du type de retour nous évite cette redondance.
Scénario 4
Enfin, il peut être utilisé à la place de fonctions très simples:
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
Parfois, cependant, nous pouvons regarder la fonction, voulant connaître le type exact, et s'il n'y est pas fourni, nous devons aller à un autre point du code, comme où pos_
est déclaré.
Conclusion
Avec ces scénarios présentés, lequel d'entre eux s'avère être une situation dans laquelle cette fonctionnalité est utile pour rendre le code plus propre? Qu'en est-il des scénarios que j'ai négligé de mentionner ici? Quelles précautions dois-je prendre avant d'utiliser cette fonctionnalité pour qu'elle ne me morde pas plus tard? Y a-t-il quelque chose de nouveau que cette fonctionnalité apporte à la table qui ne serait pas possible sans elle?
Notez que les multiples questions visent à aider à trouver des perspectives à partir desquelles y répondre.
->decltype(t+u)
par la déduction automatique tue SFINAE.Réponses:
C ++ 11 soulève des questions similaires: quand utiliser la déduction de type de retour dans lambdas et quand utiliser des
auto
variables.La réponse traditionnelle à la question en C et C ++ 03 a été "au-delà des limites des instructions, nous rendons les types explicites, dans les expressions ils sont généralement implicites mais nous pouvons les rendre explicites avec des transtypages". C ++ 11 et C ++ 1y introduisent des outils de déduction de type afin que vous puissiez omettre le type à de nouveaux endroits.
Désolé, mais vous n'allez pas résoudre ce problème dès le départ en établissant des règles générales. Vous devez examiner un code particulier et décider par vous-même si cela facilite ou non la lisibilité de spécifier des types partout: vaut-il mieux que votre code dise «le type de cette chose est X», ou est-ce mieux pour votre code pour dire, "le type de cette chose n'est pas pertinent pour comprendre cette partie du code: le compilateur a besoin de savoir et nous pourrions probablement le résoudre mais nous n'avons pas besoin de le dire ici"?
Puisque la «lisibilité» n'est pas définie objectivement [*], et de plus elle varie selon le lecteur, vous avez une responsabilité en tant qu'auteur / éditeur d'un morceau de code qui ne peut pas être entièrement satisfait par un guide de style. Même dans la mesure où un guide de style spécifie des normes, différentes personnes préféreront des normes différentes et auront tendance à trouver que tout ce qui n'est pas familier est «moins lisible». Ainsi, la lisibilité d'une règle de style proposée particulière ne peut souvent être jugée que dans le contexte des autres règles de style en place.
Tous vos scénarios (même le premier) trouveront une utilité pour le style de codage de quelqu'un. Personnellement, je trouve que le second est le cas d'utilisation le plus convaincant, mais je pense néanmoins que cela dépendra de vos outils de documentation. Il n'est pas très utile de voir documenté que le type de retour d'un modèle de fonction est
auto
, alors que le voir documenté commedecltype(t+u)
crée une interface publiée sur laquelle vous pouvez (espérons-le) compter.[*] Parfois, quelqu'un essaie de faire des mesures objectives. Dans la petite mesure où quiconque parvient à des résultats statistiquement significatifs et généralement applicables, ils sont complètement ignorés par les programmeurs en activité, en faveur de l'instinct de l'auteur de ce qui est «lisible».
la source
1.0 + 27U
nous affirmons ce dernier, lorsque nous écrivons,(double)1.0 + (double)27U
nous affirmons le premier. La simplicité de la fonction, le degré de duplication, l'évitementdecltype
pourraient tous y contribuer mais aucun ne sera décisif de manière fiable.auto
en général.auto
mot - clé, il affichera le type de retour réel.int*
, mais ce qui est réellement important, le cas échéant, c'est que la raison pour laquelle c'estint*
parce que c'est ce quistd::vector<int>::iterator_type
est avec vos options de construction actuelles!De manière générale, le type de retour de fonction est d'une grande aide pour documenter une fonction. L'utilisateur saura ce qui est attendu. Cependant, il y a un cas où je pense qu'il pourrait être intéressant de supprimer ce type de retour pour éviter la redondance. Voici un exemple:
Cet exemple est tiré du document officiel du comité N3493 . Le but de la fonction
apply
est de transmettre les éléments de astd::tuple
à une fonction et de renvoyer le résultat. Lesint_seq
etmake_int_seq
ne sont qu'une partie de l'implémentation, et ne dérouteront probablement que tout utilisateur essayant de comprendre ce qu'il fait.Comme vous pouvez le voir, le type de retour n'est rien de plus qu'un
decltype
de l'expression retournée. De plus,apply_
n'étant pas destiné à être vu par les utilisateurs, je ne suis pas sûr de l'utilité de documenter son type de retour lorsqu'il est plus ou moins le même que celuiapply
de celui-ci. Je pense que, dans ce cas particulier, la suppression du type de retour rend la fonction plus lisible. Notez que ce type de retour a en fait été abandonné et remplacé pardecltype(auto)
dans la proposition d'ajoutapply
à la norme, N3915 (notez également que ma réponse originale est antérieure à cet article):Cependant, la plupart du temps, il est préférable de conserver ce type de retour. Dans le cas particulier que j'ai décrit ci-dessus, le type de retour est plutôt illisible et un utilisateur potentiel ne gagnera rien à le connaître. Une bonne documentation avec des exemples sera bien plus utile.
Autre chose qui n'a pas encore été mentionnée: while
declype(t+u)
permet d'utiliser l' expression SFINAE ,decltype(auto)
non (même s'il y a une proposition pour changer ce comportement). Prenons par exemple unefoobar
fonction qui appellera lafoo
fonction membre d' un type si elle existe ou appellera labar
fonction membre du type si elle existe, et supposera qu'une classe a toujours exactyfoo
oubar
mais ni les deux à la fois:L'appel
foobar
sur une instance deX
s'affichefoo
lors de l'appelfoobar
sur une instance deY
s'affichebar
. Si vous utilisez la déduction de type de retour automatique à la place (avec ou sansdecltype(auto)
), vous n'obtiendrez pas l'expression SFINAE et l'appelfoobar
sur une instance de l'unX
ou l' autreY
déclenchera une erreur de compilation.la source
Ce n'est jamais nécessaire. Quant à savoir quand vous devriez - vous allez avoir beaucoup de réponses différentes à ce sujet. Je dirais pas du tout jusqu'à ce que ce soit en fait une partie acceptée de la norme et bien soutenue par la majorité des principaux compilateurs de la même manière.
Au-delà de cela, ce sera un argument religieux. Je dirais personnellement que ne jamais - mettre le type de retour réel rend le code plus clair, est beaucoup plus facile pour la maintenance (je peux regarder la signature d'une fonction et savoir ce qu'elle renvoie par rapport au fait d'avoir à lire le code), et cela supprime la possibilité que vous pensez qu'il devrait renvoyer un type et le compilateur pense qu'un autre cause des problèmes (comme cela s'est produit avec chaque langage de script que j'ai jamais utilisé). Je pense que l'automobile était une énorme erreur et qu'elle causera des ordres de grandeur plus de douleur que d'aide. D'autres diront que vous devriez l'utiliser tout le temps, car cela correspond à leur philosophie de programmation. En tout cas, cela est hors de portée de ce site.
la source
#define
s pour transformer C ++ en VB. Les fonctionnalités ont généralement un bon consensus dans l'état d'esprit du langage sur ce qui est une bonne utilisation et ce qui ne l'est pas, en fonction de ce à quoi les programmeurs de ce langage sont habitués. La même fonctionnalité peut être présente dans plusieurs langues, mais chacune a ses propres lignes directrices sur son utilisation.auto
c'est une pure bénédiction. Cela supprime beaucoup de redondance. Il est tout simplement pénible de répéter le type de retour parfois. Si vous souhaitez renvoyer un lambda, cela peut même être impossible sans stocker le résultat dans astd::function
, ce qui peut entraîner une surcharge.auto
afin qu'il corresponde toujours le type de retour de la fonction passée.auto
pour le type de retour de fin.]Cela n'a rien à voir avec la simplicité de la fonction (comme le supposait une copie maintenant supprimée de cette question).
Soit le type de retour est fixe (ne pas utiliser
auto
), soit il dépend de manière complexe d'un paramètre de modèle (à utiliserauto
dans la plupart des cas, associé àdecltype
lorsqu'il y a plusieurs points de retour).la source
Prenons un environnement de production réel: de nombreuses fonctions et tests unitaires sont tous interdépendants du type de retour de
foo()
. Supposons maintenant que le type de retour doive changer pour une raison quelconque.Si le type de retour est
auto
partout et que les appelantsfoo()
et les fonctions associées l'utilisentauto
lors de l'obtention de la valeur retournée, les modifications qui doivent être apportées sont minimes. Sinon, cela pourrait signifier des heures de travail extrêmement fastidieux et sujet aux erreurs.À titre d'exemple dans le monde réel, on m'a demandé de changer un module de l'utilisation de pointeurs bruts partout en pointeurs intelligents. La correction des tests unitaires était plus pénible que le code réel.
Bien qu'il existe d'autres façons de gérer cela, l'utilisation de
auto
types de retour semble être une bonne solution.la source
decltype(foo())
?typedef
les déclarations s et alias (c'est-à-dire lausing
déclaration de type) sont meilleures dans ces cas, en particulier lorsqu'elles ont une portée de classe.Je veux donner un exemple où le type de retour automatique est parfait:
Imaginez que vous vouliez créer un alias court pour un long appel de fonction ultérieur. Avec auto, vous n'avez pas besoin de vous occuper du type de retour d'origine (peut-être que cela changera à l'avenir) et l'utilisateur peut cliquer sur la fonction d'origine pour obtenir le type de retour réel:
PS: Cela dépend de cette question.
la source
Pour le scénario 3, je retournerais le type de retour de signature de fonction avec la variable locale à renvoyer. Cela rendrait plus clair pour les programmeurs clients que la fonction retourne. Comme ça:
Scénario 3 Pour éviter la redondance:
Oui, il n'a pas de mot-clé auto mais le principe est le même pour éviter la redondance et donner plus de facilité aux programmeurs qui n'ont pas accès à la source.
la source
vector<map<pair<int,double>,int>
, puis de l'utiliser.