10
votes

Modèle Pass par la valeur ou une référence de const ou ...??

Je peux écrire une fonction modélisée de cette façon xxx

ou de cette façon xxx

Je suppose que la deuxième option peut être plus optimale Comme il évite explicitement une copie, mais je soupçonne qu'il peut également échouer pour des types spécifiques t (par exemple, des foncteurs?). Donc, quand devrait-il utiliser la première option et quand utiliser la seconde? Il y a aussi ce boost :: call_traits :: param_type et boost :: référence_wrapper qui étaient dans les réponses à mon question précédente , mais les gens ne les utilisent pas partout, sont-ils? Y a-t-il une règle de base pour cela? Merci.


4 commentaires

Au risque de bêticulaire, je dois demander: comment pourrait-il échouer pour un foncteur?


@Beta: C'était une supposition aléatoire. Si quelqu'un pouvait expliquer dans quel cas cela peut réellement échouer, je serais intéressé.


Qu'est-ce que vous essayez d'atteindre? Cela dépend du code interne dans f .


@David Rodríguez - Dribesas: C'est plus une question générale


5 Réponses :


12
votes

Y a-t-il une règle de base pour cela?

Les mêmes règles générales pour quand utiliser Pass par référence vs Pass par valeur s'appliquent.

Si vous attendez Toujours être un type numérique ou un type très bon marché pour copier, vous pouvez prendre l'argument par valeur. Si vous allez effectuer une copie de l'argument dans une variable locale dans la fonction de toute façon, vous devez le prendre par la valeur pour Aidez le compilateur Elide des copies qui n'ont pas vraiment besoin d'être fabriquées.

Sinon, prenez l'argument par référence. Dans le cas des types peu coûteux de copier, il peut être plus coûteux, mais pour d'autres types, ce sera plus rapide. Si vous trouvez qu'il s'agit d'un hotspot de performance, vous pouvez surcharger la fonction pour différents types d'arguments et faire la bonne chose pour chacun d'eux.


7 commentaires

Donc, il n'y a pas de problèmes spécifiques à un modèle?


@ 7VIES: Pas vraiment; Le seul "problème" est que, au lieu d'avoir un seul type à penser, vous devez décider ce qui est logique pour le groupe de types avec lesquels le modèle sera utilisé.


@James McNellis: Je suppose que parfois vous ne pouvez pas décider cela, comme par exemple STL accepterait les foncteurs de valeur, tandis que l'on pourrait avoir un foncteur "lourdement étatique" et qui serait inefficace. Devrait-on utiliser que boost :: call_traits :: param_type dans ce cas ou tenez-vous à juste passer de la valeur comme STL?


@ 7VIES: Boost :: Call_Traits ne vous aidera pas parce que param_type est une référence constante pour tout type défini par l'utilisateur (et donc tous les types de classe). L'hypothèse dans la STL est que les objets de fonction sont bon marché pour copier; Si vous avez un objet de fonction coûteux à copier, la "solution droite" est de le rendre moins coûteux à copier, si possible, ou d'écrire une enveloppe qui le rend moins cher à copier.


@James McNellis: J'ai supposé (bien que jamais essayé), vous pouvez spécialiser ce trait de votre type s'il doit être transmis par la valeur.


@peterchen: Vous pouvez le spécialiser pleinement et cela pourrait être utile. Je n'ai jamais utilisé call_traits ; Il semble juste que ce soit plus de problèmes que cela vaut la peine (c'est-à-dire s'il était vraiment utile, pourquoi pas les conteneurs C ++ 0x ont été mis à jour pour utiliser des techniques similaires?).


Il apparaît que C ++ 0x Les conteneurs semblent s'appuyer sur résorfir le pilfering par des références de rvalue pour réduire le coût des copies excessives. Je souhaite que l'une de ces méthodes n'aurait pas à fier des bibliothèques étendues - ou au moins ces bibliothèques pourraient générer des diagnostics spécifiques de la compilation.



0
votes

En plus de ce que James McNellis a écrit, je veux juste ajouter que vous pouvez spécialiser votre modèle pour les types de référence ( par exemple comme celui-ci )


0 commentaires

0
votes

boost :: traits a un trait de type qui sélectionne le type "meilleur", en fonction de T:

call_traits :: param_type

Comme déjà mentionné, il n'y a pas de problèmes spécifiques à un modèle.


1 commentaires

Typiquement compilateur ne peut pas déduire l'argument des temples dans ce cas (il s'agit d'un problème spécifique au modèle).



6
votes

Je soupçonne qu'il peut également échouer pour certains types spécifiques

passe par référence-to-const est le seul mécanisme de passage qui "jamais" échoue. Il ne pose aucune exigence sur t , il accepte à la fois des avalues ​​et des avalues ​​comme des arguments et permet des conversions implicites.


0 commentaires

5
votes

Tu ne réveilleras pas les morts, mais la tête un problème similaire et voici un exemple de code qui montre comment utiliser des traits de type C ++ 11S pour déduire si un paramètre doit être transmis par la valeur ou la référence:

#include <iostream>
#include <type_traits>

template<typename key_type>
class example
{
    using parameter_type = typename std::conditional<std::is_fundamental<key_type>::value, key_type, key_type&>::type;

 public:
  void function(parameter_type param)
  {
      if (std::is_reference<parameter_type>::value)
      {
          std::cout << "passed by reference" << std::endl;
      } else {
          std::cout << "passed by value" << std::endl;
      }
  }
};

struct non_fundamental_type
{
    int one;
    char * two;
};

int main()
{
  int one = 1;
  non_fundamental_type nft;

  example<int>().function(one);
  example<non_fundamental_type>().function(nft);

  return 0;
}


0 commentaires