En C++, il est très courant de ce que je considère comme un anti-modèle qui utilise const T&
comme une façon intelligente de dire simplement" T
lorsque vous traitez avec des paramètres. Toutefois, une valeur et une référence (peu importe si const ou non) sont deux choses complètement différentes et toujours et aveuglément à l'aide de références à la place des valeurs peut conduire à des bogues subtils.
La raison en est que lorsque vous traitez avec des références vous devez tenir compte de deux questions qui ne sont pas présents avec des valeurs: la durée de vie et d'aliasing.
Juste comme exemple d'un endroit où cet anti-modèle est appliqué est le standard de la bibliothèque elle-même, où l' std::vector<T>::push_back
accepte comme paramètre une const T&
au lieu d'une valeur, ce qui peut mordre le dos par exemple dans le code comme:
std::vector<T> v;
...
if (v.size())
v.push_back(v[0]); // Add first element also as last element
Ce code est une bombe à retardement, car std::vector::push_back
veut une référence const mais faire la push_back peut exiger une réaffectation et si cela se produit signifie que, après la réaffectation de référence reçu ne serait plus valide (durée de vie ) et vous entrez dans le Comportement Indéfini royaume.
Aliasing questions sont également une source de problèmes subtils si const références sont utilisées à la place des valeurs. J'ai été mordu par exemple par le code de ce genre:
struct P2d
{
double x, y;
P2d(double x, double y) : x(x), y(y) {}
P2d& operator+=(const P2d& p) { x+=p.x; y+=p.y; return *this; }
P2d& operator-=(const P2d& p) { x-=p.x; y-=p.y; return *this; }
};
struct Rect
{
P2d tl, br;
Rect(const P2d& tl, const P2d& br) : tl(tl), bt(br) {}
Rect& operator+=(const P2d& p) { tl+=p; br+=p; return *this; }
Rect& operator-=(const P2d& p) { tl-=p; br-=p; return *this; }
};
Le code semble à première vue assez fort, P2d
est une bidimensionnelle point, Rect
est un rectangle et en ajoutant ou en supprimant un point signifie transformer le rectangle.
Si, toutefois, pour traduire le rectangle de retour à l'origine que vous écrivez myrect -= myrect.tl;
le code ne fonctionnera pas parce que la traduction de l'opérateur a été défini en acceptant une référence qui est, dans ce cas, le référencement d'un membre de la même instance.
Cela signifie que, après la mise à jour de la topleft avec tl -= p;
le topleft sera (0, 0)
comme il se doit, mais aussi p
va devenir dans le même temps, (0, 0)
car p
est juste une référence vers le haut-membre de gauche et donc la mise à jour de coin en bas à droite ne fonctionne pas car il va le traduire en (0,0)
donc faire à peu près rien.
Ne soyez pas dupé en pensant que const référence, c'est comme une valeur à cause de la parole const
. Ce mot n'existe que pour vous donner des erreurs de compilation si vous essayez de modifier l'objet référencé à l'aide de cette référence, mais cela ne signifie pas que l'objet référencé est constante. Plus précisément l'objet référencé par un const ref peut changer (par exemple à cause de l'aliasing), et peuvent même sortir de l'existence pendant que vous l'utilisez (durée de vie de l'émission).
En const T&
le mot const exprime une propriété de la référence, et non pas de l' objet référencé: c'est la propriété qui rend impossible de l'utiliser pour modifier l'objet. Probablement readonly aurait été un meilleur nom que const a de l'OMI, l'effet psychologique de pousser l'idée que l'objet va être constante pendant que vous utilisez la référence.
Vous pouvez bien sûr obtenir impressionnant la vitesse à l'aide de références au lieu de copier les valeurs, en particulier pour les grandes classes. Mais vous devriez toujours penser à l'aliasing et la durée de vie des problèmes lors de l'utilisation de références, car sous le capot, ils sont juste des pointeurs vers d'autres données.
Pour les "indigènes" des types de données (entiers, des doubles, des pointeurs) références sont cependant, en réalité, va être plus lent que les valeurs et il n'y a rien à gagner à les utiliser à la place des valeurs.
Aussi const référence signifiera toujours des problèmes pour l'optimiseur que le compilateur est forcé d'être paranoïaque et chaque fois qu'un inconnu code est exécuté, il doit l'assumer que tous les objets référencés peuvent avoir une valeur différente (const
pour une référence signifie absolument RIEN pour l'optimiseur; ce mot est-il que pour aider les programmeurs à - personnellement, je suis pas si sûr, c'est d'une grande aide, mais c'est une autre histoire).