int vs const int&
J'ai remarqué que j'ai l'habitude d'utiliser références constantes au retour des valeurs ou des arguments. Je pense que la raison est qu'il fonctionne presque le même que l'utilisation non référence dans le code. Mais ça prend plus de place et les déclarations de fonction de plus longue durée. Je suis OK avec ce code mais je pense que certaines personnes de mon trouver un mauvais style de programmation.
Qu'en pensez-vous? Vaut-il écrit const int& sur int? Je pense que c'est optimisé par le compilateur de toute façon, alors peut-être je suis juste de perdre mon temps à coder ça, une?
- comment pourrait-il en être optimisé par le compilateur? Si la fonction est incorporé au compilateur qu'il pourrait, mais pour le cas général où il n'est pas, il doit revenir exactement ce que vous avez dit (sauf quelques exceptions), car vous pourriez être appelé à partir d'une autre unité de traduction à l'aide des prototypes, de sorte qu'il doit être appelé avec int/int const& (sur le niveau de l'assemblée du passage d'un int pour quelque chose dans l'attente int& n'est-ce pas une bonne idée). Combinez cela avec le fait que const int& est probablement plus lent que int (à cause de la deref et la copie bon marché), et vous devriez maintenant quoi faire.
- Pour que l'optimiseur une
const T&
est justeT&
, en d'autres termes, juste un pointeur constant, éventuellement, un changement de T objet. Leconst
mot est visible que par les programmeurs. - Eh bien, et par le compilateur de la face avant, qui suit si le programmeur ne tient pas compte du
const
. - ++ pour écrire la dernière phrase avec l'accent Canadien.
Vous devez vous connecter pour publier un commentaire.
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 direT
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: vie et aliasing.
Comme un exemple d'un endroit où cet anti-modèle est appliqué est le standard de la bibliothèque elle-même, où
std::vector<T>::push_back
accepte comme paramètre uneconst T&
au lieu d'une valeur, ce qui peut mordre le dos par exemple dans le code comme:Ce code est une bombe à retardement, car
std::vector::push_back
veut const référence 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 (vie question) 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:
Le code semble à première vue assez sûr,
P2d
est une bidimensionnelle point,Rect
est un rectangle et en ajoutant ou en supprimant un point signifie transformer le rectangle.Si toutefois de 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 (en l'occurrence) fait référence à un membre de la même instance.Cela signifie que, après la mise à jour de la topleft avec
tl -= p;
la topleft sera(0, 0)
comme il se doit, mais aussip
deviendra en même temps(0, 0)
parce quep
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 par(0, 0)
donc faire à peu près rien.S'il vous plaît ne vous laissez pas berner 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, en raison de aliasing), et peuvent même sortir de l'existence pendant que vous l'utilisez (vie question).Dans
const T&
le mot const exprime une propriété de la référence, pas de la 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.
Également 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).push_back()
pourrait être tellement sinistre!std::vector
. Juste après réaffectation les données seront copiées puisv[0]
sera OK?std::vector::push_back
que le code est de passer une référence vers le premier élément du vecteur, en d'autres termes une adresse en mémoire où cet élément est stocké. Lepush_back
code peut-être besoin de réaffecter le vecteur de contenu pour faire de la place pour un nouvel élément et après l'avoir fait que réaffectation il copie construire une nouvelle entrée à l'aide de l'adresse source. Toutefois, si une allocation qui est arrivé à l'adresse sera celle d'un objet mort qui a été réaffectée à un autre endroit. C'est un problème de durée de vie... au cours de l'exécution depush_back
l'objet a été détruit.push_back
problème un problème qui est résolu par la méthode (de sorte qu'il est codé, par exemple, en interne, en faisant une copie supplémentaire quand il a à realloc), ou est-il une "fonctionnalité" des utilisateurs depush_back
doit savoir?push_back
ainsi passer àpush_back
un élément de la même vecteur est une violation du contrat.int foo(const int& x, std::vector<int>& y) { y.pop_back(); return x; }
UB lorsqu'il est appelé commefoo(a.back(), a)
parce que même six
est unconst X&
l'objet référencé est détruit avant d'être consulté. Pour créer un alias considérerint foo(const int& x, int& y)
et appelé avecfoo(a, a)
... sifoo
code de mutationy
la mutation apparaît également dansx
(malgré qu'il soit déclaréconst int&
). C'est exactement ce que provoque le bug subtil dans leRect
exemple de classe.Comme dit Oli, de retour d'un
const T&
par opposition àT
sont deux choses complètement différentes, et peuvent se briser dans certaines situations (comme dans son exemple).Prendre
const T&
par opposition à la plaine deT
comme un argument est moins susceptible de casser des choses, mais encore plusieurs différences importantes.T
au lieu deconst T&
exige queT
la copie est constructible.T
appeler le constructeur de copie, ce qui peut être coûteux (et aussi le destructeur sur la sortie de la fonction).T
vous permet de modifier le paramètre comme une variable locale (peut être plus rapide que la copie manuelle).const T&
pourrait être plus lente due à un défaut d'alignement temporaires et le coût d'indirection.T
est un petit type, tels queint
, la copie pourrait être moins cher que celle de la référence.const T&
peut aussi exiger queT
la copie est constructible; voir ici stackoverflow.com/questions/4733448/...int &
etint
ne sont pas interchangeables! En particulier, si vous renvoyer une référence à une pile locale variable, le comportement est indéfini, par exemple:Vous peut renvoyer une référence à quelque chose qui ne sera pas détruite à la fin de la fonction (par exemple, un statique, ou un membre de la classe). C'est donc valide:
et le monde extérieur, a le même effet que le retour de la
int
directement (à l'exception que vous pouvez maintenant le modifier, c'est pourquoi vous voyezconst int &
beaucoup).L'avantage de la référence est qu'aucune copie n'est nécessaire, ce qui est important si vous faites affaire avec de grands objets de la classe. Cependant, dans de nombreux cas, le compilateur peut optimiser ce que l'écart; voir, par exemple,http://en.wikipedia.org/wiki/Return_value_optimization.
int
, il n'y a certainement aucun avantage sur n'importe quel sane plate-forme.Si l'appelé et de l'appelant sont définies à séparer les unités de compilation, alors le compilateur ne peut pas optimiser loin la référence. Par exemple, j'ai compilé le code suivant:
avec G++ sous Linux 64 bits à l'optimisation de niveau 3. Le premier appel a besoin d'aucun accès à la mémoire principale:
Ligne n ° 1 utilise directement la valeur de retour dans
eax
comme argument pourtest1
dansedi
. La ligne #2 et #3 poussez le résultat dans la mémoire principale et de la place de l'adresse dans le premier argument, car l'argument est déclaré en tant que référence de type int, et donc il doit être possible par exemple de prendre son adresse. Si quelque chose peut être entièrement calculé en utilisant les registres ou les besoins d'accès à la mémoire principale peut faire une grande différence ces jours-ci. Donc, en plus d'être plus à taper,const int&
peut également être plus lent. La règle de base est, transmettre toutes les données qui est plus grande que celle de la taille de mot en valeur, et tout le reste par référence const. Également passer basé sur un modèle d'arguments par référence const; car le compilateur a accès à la définition du modèle, il peut toujours optimiser la référence à l'écart.Au lieu de "penser" c'est optimisé par le compilateur, pourquoi ne pas vous obtenez l'assembleur d'inscription et de trouver pour vous?
indésirable.c++:
Généré assembleur de sortie (élidée):
...
La
movl
des instructions dans les deux sont très différents. Le premier se déplace5
enEAX
(qui se trouve être le registre traditionnellement utilisé pour les valeurs de retour en x86 code C) et le second se déplace l'adresse d'une variable (détails gommés pour plus de clarté) dansEAX
. Cela signifie que la fonction d'appel dans le premier cas peut juste utiliser directement registre des opérations sans frapper la mémoire à utiliser la réponse tandis que dans le second, il a frappé la mémoire à travers le pointeur retourné.Donc, on dirait que c'est pas optimisé loin.
C'est plus que les autres réponses que vous avez été donné ici d'expliquer pourquoi
T
etconst T&
ne sont pas interchangeables.int est différents avec const int&:
2, int est la valeur de la copie d'une autre variable de type entier (int B), ce qui signifie: si nous changeons int B, la valeur de type int ne changera pas.
Voir le code c++ suivant:
int main(){
un vecteur{1,2,3};
int b = a[2];//la valeur change pas, même lorsque vecteur de changement
const int& c = a[2];//c'est la référence, de sorte que la valeur dépend de vecteur;
a[2]=111;
//b sortie 3;
//c de sortie 111;
}