2010-07-22 3 views
14

J'ai toujours appris que les types non primitifs doivent être passés par référence const plutôt que par la valeur, si possible, à savoir:De petites structures simples devraient-elles être transmises par référence const?

void foo(std::string str);//bad 
void foo(const std::string &str);//good 

Mais je pensais aujourd'hui que peut-être en fait des types définis par l'utilisateur simples peuvent effectivement être mieux passé par valeur, par exemple:

class Vector2 
{ 
public: 
    float x, y; 
    ...constructors, operators overloads, utility methods, etc... 
}; 

void foo(Vector2 pos); 
void foo(const Vector2 &pos);//is this really better than by value? 
void foo(float x, float y);//after all isn't by value effectively doing this? 

Ma pensée est que, en passant le Vector2 par référence, il est en fait plus cher que de passer par la valeur depuis le compilateur utilise maintenant un pointeur et déréférencement pour accéder à la const Vector2 & pos version?

Est-ce le cas? Les objets simples sont-ils les mieux réussis en valeur? Où la ligne devrait-elle être dessinée?

+0

Notez que si votre type de classe a des constructeurs déclarés par l'utilisateur, ce n'est pas POD. –

+6

La règle générale: Passer par const-référence, sauf si 'sizeof (T) <= sizeof (void *)' ou 'T' est primitif. – GManNickG

+2

@GMan, tu m'as battu dessus (et +1). Bien, je généraliserais à c'est probablement inférieur ou égal au mot-taille du processeur. Et, il faut vérifier les spécifications du processeur pour trouver la taille optimale. Si je ne me trompe pas, les compilateurs pourraient potentiellement optimiser un ref const à une valeur de passe dans certains cas, indépendamment de la taille. J'imagine que ceci est particulièrement vrai en C++ 0x étant donné la nouvelle sémantique de déplacement. –

Répondre

7

Oui, les objets simples doivent être passés en valeur. La ligne doit être tracée en fonction de l'architecture. En cas de doute, profil.

+0

+1 pour le profilage - c'est le seul moyen de savoir lequel est le meilleur –

1

Passer par la référence const évite la construction d'un nouvel objet. Si votre constructeur est non trivial, par exemple, il alloue de la mémoire, vous serez beaucoup mieux vaut passer par la référence const que par la valeur.

Dans le monde C#, vous prenez cette décision en choisissant de créer une classe ou une structure. Les classes utilisent la sémantique de référence tandis que les structures utilisent la sémantique des valeurs. Tout ce que j'ai jamais lu dit que vous devriez généralement choisir de tout faire une classe à moins qu'elle ne soit très petite, c'est-à-dire de l'ordre de 16 octets. Si vous cherchez une limite pour quand passer par la valeur par rapport à la référence const en C++, 16 octets semble être un seuil raisonnable.

+1

Les types POD n'ont pas de constructeurs de copie non triviaux (ou bien des constructeurs ou des destructeurs non triviaux par défaut). –

+2

L'OP n'a pas dit explicitement le type POD. Il a dit ** simple ** type défini par l'utilisateur et a ensuite donné un exemple qui indiquait qu'il avait un constructeur. –

0

Ma pensée est que, en passant le Vector2 par référence, il est en fait plus cher que de passer par la valeur depuis le compilateur utilise maintenant un pointeur et déréférencement pour accéder à la const Vector2 & version pos

Ce serait vrai si vous passiez un objet où size_of(object) < size_of(pointer_to_object). Par exemple, char const& peut être plus cher que char

+2

Cela peut également être vrai si le déréférencement répété entraîne une surcharge plus importante que celle qui est enregistrée en ne transmettant pas autant de données. Ou si la référence empêche les optimisations en raison de problèmes d'alias. Ou si la référence blesse votre localité. –

1

Pour des raisons de politique, je passe tout objet qui n'est pas un type de données C de base par référence const. C'est beaucoup plus facile de se souvenir de cette façon.

1

Une analyse plus ancienne, mais claire, des entrées/sorties des paramètres de passage peut être trouvée à http://www.ddj.com/184403855. Bien sûr, C++ 0x évite beaucoup de problèmes de ce type avec la sémantique des déplacements, mais le document fournit beaucoup de justification pour expliquer pourquoi la sémantique des déplacements est souhaitable.

1

Un facteur que je n'ai pas vu mentionné est ce que la routine va faire avec la valeur transmise. À moins que la routine ne soit développée en ligne, la manipulation de données transmises par référence nécessitera plus de code que la manipulation de données transmises par valeur. Si l'on accède en moyenne aux champs de la structure transférée moins d'une fois chacun, cette surcharge supplémentaire sera faible par rapport à la surcharge de la copie de la structure. Si l'on accède en moyenne plusieurs fois à chacun, il serait probablement préférable de les avoir sur la pile. Si l'interface est déjà définie comme référence par renvoi pour une structure fortement sollicitée, il peut être logique que la routine appelée copie toutes les parties qui l'intéressent. D'un autre côté, si la routine appelée va copier tout ou partie d'une structure, elle peut aussi bien être passée en valeur.

Questions connexes