Question

Si un C ++ (implicite ou explicite) constructeur de valeurs accepte le paramètre (s) par valeur ou référence à const, quand il a besoin de stocker une copie de l'argument (s) dans son objet ou l'autre manière?

Voici les plus brefs exemple, je peux penser à:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

L'idée ici est que je veux minimiser les appels au constructeur de copie de bar lorsqu'un objet foo est créé, dans l'une des différentes façons dont un objet foo peut se créer.

S'il vous plaît noter que je connais un peu la copie élision et (nommé) Valeur de retour d'optimisation, et j'ai lu "voulez-vous Speed? Passez par la valeur" , mais je ne pense pas que l'article traite directement ce cas d'utilisation.

Edit:. Je devrais être plus précis

Supposons que je ne peux pas savoir le sizeof(bar), ou si oui ou non bar est un élément fondamental, type intégré (bar peut être un paramètre de modèle, et foo peut-être un modèle de classe au lieu d'une classe). En outre, ne présumez pas le constructeur de ce foo peut être inline (ou années bar, pour cette matière). Ne présumez que je au moins susceptibles d'utiliser un compilateur qui implémente RVO.

Ce que je voudrais est qu'il y ait une possibilité (données des optimisations du compilateur) qu'un appel comme celui-ci invoquera aucun appel au constructeur de copie de bar que ce soit (même lors de l'exécution _b(b) dans la liste d'initialisation de foo):

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

Est-il possible (étant donné la norme C ++ 98) que cela peut être fait, et si oui, est-il plus ou moins susceptibles de travailler si foo accepte son paramètre par référence à const au lieu de par sa valeur?

Était-ce utile?

La solution

En C ++ 98 et 03 C ++, vous devez passer const& bar puis copier. En C ++ 0x, vous devez passer bar puis faire un mouvement (fourni bar a un constructeur de déplacement).

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

Si vous construisez foo avec un paramètre lvalue, le constructeur de copie sera appelé à créer une copie b et cette copie sera déplacé dans _b. Si vous construisez foo avec un paramètre rvalue, constructeur de déplacement de bar sera appelée à se déplacer dans b, puis il sera déplacé à nouveau en _b.

Autres conseils

Toutes choses étant égales par ailleurs, je passe par const & pour les classes suffisamment complexes, et la valeur de POD et des objets simples.

Couché les avantages / inconvénients de passer par référence const au lieu de passer par la valeur traditionnelle

Positifs:

  • Evite une copie (gros plus pour les objets avec une copie cher)
  • Accès en lecture seule

Négatifs:

  • Quelqu'un peut-const jeter le const au large de la référence s'ils voulaient vraiment

Plus important encore avec les points positifs est vous explicitement contrôle lorsque la copie arrive (dans votre cas lors du passage de l'initialisation _b dans votre liste de initialiseur). La réflexion sur les points négatifs ... Je suis d'accord qu'il ya un risque. Je pense que presque tous les bons programmeurs se sentent sales à propos emplying le const_cast. En outre, vous pouvez chercher docilement const_cast et jeter des tomates à la personne jetant const hors l'argument. Mais bon on ne sait jamais et qui a le temps de regarder le code comme un faucon?)

Mon opinion subjective est que dans les classes suffisamment complexes et dans des environnements où les performances compte l'avantage d'éviter le constructeur de copie l'emporte sur le risque. Cependant, pour les classes vraiment stupide et POD, je tends à faire des copies des données et les transmettre par valeur.

Regardez cette question .

Utilisez const T & arg si sizeof(T)>sizeof(void*) et utiliser T arg si sizeof(T) <= sizeof(void*). Tous les types fondamentaux devraient être l'exception à cette règle

Stylistiquement, je dirais que le passage par référence est la meilleure façon.

Si la performance est vraiment important, alors ne pas deviner. Mesurer.

Je n'ai pas vérifié ce que dit la norme, mais en essayant empiriquement je peux vous dire que GCC n'optimise pas la copie loin, que le constructeur accepte l'argument par valeur ou par référence const.

Si, cependant, le constructeur prend une référence const, il parvient à éviter une copie inutile lorsque vous créez foo à partir d'un existant bar objet.

Pour résumer:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

Non pas que je suis un expert du compilateur, mais je suppose qu'il est logique qu'il ne peut généralement pas RVO une valeur de retour dans un lieu arbitraire (tel que le champ de membre du foo objet). Au lieu de cela, il faut que la cible soit sur le dessus de la pile.

Je suppose bar a un constructeur de copie normale du formulaire bar(const bar &b)

Prenez une référence const ici. Le constructeur de bar prendra alors cette référence et effectuer la copie. Total des copies: 1.

Si vous avez quitté la référence au large, le compilateur faire une copie de b, passer la copie au constructeur de foo, qui serait alors le transmettre à bar et copier cela.

Honnêtement, pour ce cas, la meilleure option est de rendre votre constructeur inline, le constructeur fourni bar ne jette pas. Ensuite, il suffit de passer par référence.

En outre, comme vous vous en doutez, votre article ne concerne pas ici.

Tenir un bar à shared_pointer dans votre classe et passer en tant que tel. De cette façon, vous appelez jamais le constructeur de copie:)

.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top