Je réalise donc que const T&
et T const&
sont identiques et qu’ils signifient une référence à un const T. Dans les deux cas, la référence est également constante (les références ne peuvent pas être réaffectées, contrairement aux pointeurs). J'ai observé, dans mon expérience quelque peu limitée, que la plupart des programmeurs C++ utilisent const T&
, mais j'ai rencontré quelques personnes qui utilisent T const&
. J'utilise const T&
simplement parce que je l'ai appris ainsi, et donc T const&
me semble un peu drôle. Quelle est la raison pour laquelle vous utilisez la variante que vous utilisez? L'un de vous travaille-t-il dans une organisation pour laquelle les normes de codage prescrivent l'utilisation d'une variante plutôt que l'autre?
Modifier
D'après les réponses, il semblerait qu'une des raisons pour choisir entre les deux est de savoir si vous voulez le lire comme le compilateur (de droite à gauche) ou comme l'anglais (de gauche à droite). Si on le lit comme le compilateur, alors "T const &" se lit comme "& (référence) const (à une constante) T (de type T)". Si on le lit comme l'anglais, de gauche à droite, alors "const T &" est lu comme "un objet constant de type T sous la forme d'une référence". Je préfère le lire comme de la prose anglaise, mais je peux certainement comprendre le sens en l’interprétant comme le compilateur.
Personne n'a répondu à la question sur l'organisation ou les normes de codage, mais je soupçonne fortement que la plupart des organisations ne les mandatent pas, même si elles pourraient rechercher la cohérence.
Je pense que certaines personnes préfèrent simplement lire les déclarations de droite à gauche. const
s'applique au jeton de gauche, sauf lorsqu'il n'y a rien et que cela s'applique au jeton de droite. Ainsi, const T&
implique la clause "sauf" et peut peut-être être considéré comme plus compliqué (en réalité, les deux devraient être aussi faciles à comprendre).
Comparer:
const T* p; (pointer to T that is const)
T const* p; (pointer to const T) //<- arguable more natural to read
T* const p; (const pointer to T)
Cela fera une différence si vous avez plus d'un modificateur const/volatile. Ensuite, le mettre à la gauche du type est toujours valide, mais la cohérence de la déclaration entière sera brisée. Par exemple:
T const * const *p;
signifie que p est un pointeur sur const pointeur sur const T et que vous lisez toujours de droite à gauche.
const T * const *p;
signifie la même chose mais la cohérence est perdue et vous devez vous rappeler que le plus à gauche const/volatile est lié à T seul et non à T *.
Si vous trouvez cette discussion intéressante, vous trouverez probablement un article this de Dan Saks intéressant. Cela ne répond pas directement à votre question, mais explique pourquoi il préfère
VP const foo[];
à
const VP foo[];
C'est parce que
typedef void *VP;
vous pourriez facilement être induit en erreur en pensant que le deuxième exemple ci-dessus signifie
const void *foo[]; // Wrong, actually: void *const foo[];
mais le premier exemple est plus difficile à mal interpréter.
Mon raisonnement est le suivant:
Si vous écrivez "const T &", il semble que vous fassiez mieux entendre la langue, mais vous obtenez alors la référence ambiguë "constante T". J'ai déjà constaté plusieurs fois que le code comprenait mal le code qui permettait à une personne, même semi-expérimentée, de mal interpréter le sens de quelque chose ou de déclarer un type plus complexe.
Je ne peux penser à aucun exemple pour le moment, mais plus d'une fois, j'ai répondu à des questions sur les déclarations de type et la constance, où le problème était causé par l'habitude d'utiliser "const T &" au lieu de "T const &". Je l’écrivais aussi de cette façon et quand je suis devenu développeur senior, responsable du mentorat et de la création de normes de code dans les projets, j’ai trouvé beaucoup plus facile pour les développeurs débutants d’obliger tout le monde à utiliser "T const &". Je suppose qu'une erreur de recrue plutôt triviale serait pourquoi ce code est compilé?
const T* t = f();
t = 0; // assignment to const?? - no, it is not the T* that is const, just the T.
Lorsque vous apprenez à le lire comme le compilateur, il devient beaucoup plus facile de comprendre ce que représente un type complexe donné et de vous permettre de déclarer plus facilement des types complexes lorsque vous en avez besoin. Par types complexes, je parle de choses telles que:
T const * const &
Lorsque vous savez que le compilateur lit de droite à gauche, de fond en bout, ce que cela signifie devient assez évident et s’il est nécessaire d’en écrire un, vous pouvez le faire facilement: référence au pointeur constant sur une constante T. Maintenant, écrivez la déclaration d’un "référence à un pointeur sur un pointeur constant sur un T". Si vous utilisez simplement la notation de gauche à droite, je pense que vous trouverez cela assez facile.
En bref, s’il semble au début peu naturel de s’apprendre à utiliser TOUJOURS la grammaire droite-> gauche, au lieu de ne le faire que lorsque cela est nécessaire (parce que c’est souvent le cas), il vous sera beaucoup plus facile de vous rappeler ce que signifie une ligne de code. et comment écrire ce que vous voulez dire. C'est en quelque sorte la même raison pour laquelle je refuse "," dans les déclarations:
T* ptr1 = 0, ptr2 = 0; // oops!!!// do it this way please!
T* ptr1 = 0;
T* ptr2 = 0;
Techniquement, c'est la même chose, mais lorsque vous essayez de faire travailler un groupe de personnes de capacités différentes sur la même chose, vous avez tendance à vous assurer que tout le monde utilise la méthode la plus facile à comprendre et à utiliser. Mon expérience m'a appris que "T const &" est cette méthode.
Je pense que c'est une préférence personnelle. Il n'y a pas de différence entre les deux variantes.
Étant donné que le code est principalement basé sur l'anglais, les programmeurs ont tendance à lire de gauche à droite, donc const T&
nous lit naturellement à l'endroit où le compilateur le lit à l'envers pour que T const&
se lise naturellement (référence à un const T)
C'est parce que certains trouvent utile de lire la déclaration de droite à gauche.
char const*
const char*
sont tous les deux un pointeur sur const char.
J'avais l'habitude d'être un ardent défenseur de const T&
car il lit logiquement de gauche à droite (c'est une constante T référence). (Et il y a probablement un parti pris puisque la plupart du code que j'avais rencontré à ce moment-là était écrit de cette façon.)
Au fil des années, j'ai rencontré des cas critiques (tels que plusieurs couches de pointeur/référence, plusieurs déclarations de variable et des pointeurs de méthode) qui alourdissent un peu les choses pour les raisons pour lesquelles d'autres personnes ont déjà répondu. L'introduction de typedefs supplémentaires vous aide souvent à "décoller" ces cas, mais vous devez ensuite donner un nom à quelque chose même s'il n'est utilisé qu'une seule fois.
Le point critique pour moi a été l’introduction de auto
dans C++ 11, en particulier lorsqu’il est combiné avec range-based-for. Avec cela, j'ai inversé et maintenant je préfère fortement T const&
(ou "référence à constante T", en lisant de droite à gauche) à la place. Non seulement cela est plus cohérent avec la façon dont le compilateur analyse réellement, cela signifie que lorsque vous remplacez le type par auto
, cela se termine toujours à gauche, ce qui, je pense, se lit mieux.
Comparer:
for (const T& a : list) for (T& a : list)
for (T const& a : list) for (T& a : list)
for (const auto& a : list) for (auto& a : list)
for (auto const& a : list) for (auto& a : list)
Notez également la colonne de droite, où j'ai ajouté la version non-const du même. Au moins pour moi, les const/vs-const et auto/explicite semblent tous les plus cohérents dans les cas où const
apparaît après T.
Mais il s’agit d’un choix de style et, en tant que tel, il n’ya pas de réponse absolument correcte.
Si la const
et le &
se séparent, comme dans
krog::FlamBlott<std::map<HurkleKey,Spleen<int>>
speckleFlams( const krog::Floonage & floon, const std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap);
krog::FlamFinagleReport
finagleFlams( const krog::Floonage & floon, std::map<krog::HackleSpoon,std::vector<krog::HeckleObservation<OBSN>>> & obsmap)
... alors il devient facile de rater le fait que le premier 'obsmap' est une référence const et que le second ne l'est pas.