2015-09-27 3 views
0

J'ai donc une fonction qui doit prendre un std :: vector comme paramètre. J'aimerais connaître la meilleure façon de déclarer le paramètre afin que le tableau sous-jacent ne soit pas copié en profondeur, car il pourrait être plutôt volumineux.C++ lvalues, rvalues, références, paramètres et performance

// which should I choose? 
void myFunc(std::vector<char>); // 1 
void myFunc(std::vector<char>&); // 2 
void myFunc(std::vector<char>&&); // 3 
void myFunc(std::vector<char>*) // 4 

Que devrais-je choisir? En outre, je ne vais pas modifier le vecteur dans la fonction, donc je ne devrais pas ajouter const? Dois-je surcharger la fonction et avoir une combinaison de ceux-ci?

+2

Si vous n'avez pas besoin de le modifier, alors 'const std :: vector &'. –

+0

Cela fonctionnera-t-il pour les valeurs? – dwoodwardgb

+0

en outre, probablement vous avez oublié _cv-qualificateurs _... – Nevermore

Répondre

5
  1. Si vous allez faire une copie de celui-ci à l'intérieur de la fonction de toute façon:

    void myFunc(std::vector<char>); 
    
  2. Si vous voulez juste lire l'argument sans le copier:

    void myFunc(const std::vector<char>&); 
    
  3. Si vous voulez modifier le vecteur original passé à la fonction:

    void myFunc(std::vector<char>&); 
    
  4. Si vous souhaitez optimiser pour rvalues ​​ou si vous voulez move l'argument dans la fonction:

    void myFunc(std::vector<char>&&); 
    
  5. Si vous devez être en mesure de signifier un argument optionnel passé par référence:

    void myFunc(const std::vector<char>*); 
    
  6. Si vous devez passer un argument optionnel que vous souhaitez modifier si non nullptr:

    void myFunc(std::vector<char>*); 
    
+0

Selon cette réponse http://stackoverflow.com/questions/6368690/rvalue-function-overloading option 1 sera optimisé pour les valeurs ... donc je suis un peu confus. – dwoodwardgb

+0

@dwoodwardgb Oui. C'est exactement pourquoi il devrait être utilisé. Vous obtenez une fonction qui gère efficacement les valeurs et les valeurs. – emlai

+0

Ok, mais comment fonctionne l'option 1 pour les lvalues? Ne va-t-il pas appeler le constructeur de copie pour un vecteur std ::? – dwoodwardgb

2

Si vous ne pas veulent copie complètestd::vector:

  • mouvement dans votre fonction:

    // foo() decalaration 
    void foo(std::vector<int> v); 
    
    // usage example 
    std::vector<int> v {0, 1, 2, 3}; 
    foo(std::move(v)); 
    // v is moved into foo() and invalid now 
    

    Vous pouvez également retourner ce vecteur de la fonction de la même manière:

    // foo() decalaration 
    std::vector<int> foo(std::vector<int> v) { 
        return v.push_back(4), std::move(v); 
    } 
    
    // usage example 
    std::vector<int> v {0, 1, 2, 3}; 
    v = foo(std::move(v)); 
    // now v is {0, 1, 2, 3, 4} and no one were deep copied 
    

    Mais notez si vous ne déplacer il (appel foo(v) au lieu de foo(std::move(v))) alors il sera copié profonde. Sous le capot, le paramètre v de foo() est simplement construit par move-constructor.

  • passe comme référence:

    // foo() declaration 
    void foo(std::vector<int>& v); 
    

    Mais maintenant, nous avons un problème: qui référence et cv-qualifiés?Eh bien, en général, nous avons 2 types de références et 4 types de cv-qualifiés, au total 8 déclarations:

    void foo(std::vector<int>&); 
    void foo(std::vector<int> const&); 
    void foo(std::vector<int> volatile&); 
    void foo(std::vector<int> const volatile&); 
    void foo(std::vector<int>&&); 
    void foo(std::vector<int> const&&); 
    void foo(std::vector<int> volatile&&); 
    void foo(std::vector<int> const volatile&&); 
    

    Bien sûr, une partie d'entre eux sont inutiles et devraient être supprimés. Mais néanmoins trop de déclarations aussi connu comme problème de transfert parfait (en fait, il n'y avait pas de références rvalue quand il y avait un problème donc le problème était 2 fois plus petit).

    Par exemple, si vous souhaitez modifier v vous avez besoin de 2 fonctions au moins:

    void foo(std::vector<int>&); 
    void foo(std::vector<int>&&); 
    

    Dans ce cas, vous pourrez appeler foo() sur des objets lvalue:

    std::vector<int> v; 
    foo(v); 
    

    ainsi comme sur temporaire:

    foo(std::vector<int>{1, 2, 3, 4, 5}); 
    

    Mais comment code juste une implémentation pour différents types de référence et/ou cv-qualificatifs? Permettez-moi de vous présenter références universelles:

    template<typename Vector> 
    void foo(Vector&& v); 
    

    Vector&& est-toujours un type de référence et peut être déduit dans

    • std::vector<int>& si vous passez lvalue de type std::vector<int> dans foo():

      std::vector<int> v; 
      foo(v); // v is lvalue 
      
    • std::vector<int> const& si vous passez const lvalue de type std::vector<int>:

      std::vector<int> const v; 
      foo(v); // v is const lvalue 
      
    • std::vector<int>&& si vous passez rvalue:

      foo(std::vector<int>{0, 1, 2}); // v is rvalue 
      
    • etc ...

    Mais dans ce cas, vous avez pour vérifier l'acceptation du type Vector. Mais c'est une autre histoire.

Et je ne trouve absolument pas de sens de passer des pointeurs au lieu de références dans ce cas.

+0

Merci pour la réponse complète! Je ne connaissais pas l'ensemble des références universelles. J'ai fini par aller simplement avec myFunc (const std :: vector &) parce que je n'avais pas du tout besoin de modifier le vecteur. – dwoodwardgb

+0

@dwoodwardgb Oui, cela dépend des objectifs. De plus, 'auto &&' est aussi une référence universelle. – Nevermore