222 votes

Pourquoi utiliser des non-membre de début et de fin de fonctions en C++11?

Chaque conteneur standard a un begin et end fonction pour le retour des itérateurs pour ce conteneur. Cependant, C++11 a, apparemment, a introduit la gratuité des fonctions appelées begin et end qui appellent l' begin et end des fonctions de membre du. Donc, au lieu d'écrire

auto i = v.begin();
auto e = v.end();

vous voulez écrire

auto i = begin(v);
auto e = end(v);

Dans son discours, l'Écriture de C++Moderne, Herb Sutter dit que vous devriez toujours utiliser les fonctions libres de maintenant, quand vous voulez le début ou la fin d'itérateur pour un conteneur. Cependant, il ne rentre pas dans le détail pourquoi vous voulez. En regardant le code, il vous permet d'économiser tous d'un seul caractère. Donc, tant que le conteneur standard de go, la gratuit des fonctions semblent être complètement inutile. Herb Sutter a indiqué qu'il y avait des avantages pour les non-conteneurs standard, mais encore une fois, il n'a pas entrer dans les détails.

Donc, la question est quoi exactement la fonction libre versions d' begin et end faire au-delà de l'appel de leur membre correspondant de la fonction des versions, et pourquoi voudriez-vous utiliser?

176voto

Matthieu M. Points 101624

Comment appelez-vous .begin() et .end() sur un C-ensemble ?

Gratuit-fonctions permettent plus de programmation générique, car ils peuvent être ajoutés par la suite, sur une structure de données ne permet pas de modifier.

37voto

deft_code Points 19418

À l'aide de l' begin et end libre les fonctions en ajoute une couche d'indirection. Habituellement, c'est le fait de permettre une plus grande flexibilité.

Dans ce cas, je peux penser à quelques utilisations.

Utilisation la plus évidente est pour le C-tableaux (pas c les pointeurs).

Un autre est lorsque vous essayez d'utiliser un algorithme standard sur un non-conforme conteneur (c'est à dire le conteneur est manquante .begin() méthode). En supposant que vous ne pouvez pas simplement le corriger le conteneur, la meilleure option consiste à surcharger l' begin fonction. Herb est ce qui suggère que vous utilisez toujours l' begin fonction de promouvoir l'uniformité et de la cohérence dans votre code. Au lieu d'avoir à se souvenir des conteneurs de soutien méthode begin et qui ont besoin de la fonction begin.

En aparté, le prochain C++ rev devrait copie D pseudo-membre de la notation. Si a.foo(b,c,d) n'est pas défini au lieu de cela, il essaie foo(a,b,c,d). C'est juste un peu de sucre syntaxique pour nous aider à nous, pauvres humains qui préfèrent l'objet, puis le verbe de la commande.

36voto

GreenScape Points 1136

Considérer le cas où vous avez de la bibliothèque qui contiennent de la classe:

class SpecialArray;

il dispose de 2 méthodes:

int SpecialArray::arraySize();
int SpecialArray::valueAt(int);

pour effectuer une itération sur les valeurs que vous devez hériter de cette classe et de définir begin() et end() méthodes pour les cas de

auto i = v.begin();
auto e = v.end();

Mais si vous utilisez toujours la

auto i = begin(v);
auto e = end(v);

vous pouvez faire ceci:

template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, 0);
}

template <>
SpecialArrayIterator end(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, arr.arraySize());
}

SpecialArrayIterator est quelque chose comme:

class SpecialArrayIterator
{
   SpecialArrayIterator(SpecialArray * p, int i)
    :index(i), parray(p)
   {
   }
   SpecialArrayIterator operator ++();
   SpecialArrayIterator operator --();
   SpecialArrayIterator operator ++(int);
   SpecialArrayIterator operator --(int);
   int operator *()
   {
     return parray->valueAt(index);
   }
   bool operator ==(SpecialArray &);
   // etc
private:
   SpecialArray *parray;
   int index;
   // etc
};

maintenant, i et e peut être légalement utilisé pour l'itération et d'accéder à des valeurs de SpecialArray

18voto

Nate Points 51

Pour répondre à votre question, la gratuit des fonctions begin() et end() par défaut ne rien faire d'autre que d'appeler les conteneur des membres .begin() et .fin (). D' <iterator>, inclus automatiquement lorsque vous utilisez l'un des conteneurs standard comme <vector>, <list>, etc., vous obtenez:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());
template< class C > 
auto begin( const C& c ) -> decltype(c.begin()); 

La deuxième partie de votre question est pourquoi préférer les fonctions libres si elles ne font appeler les fonctions de membre de toute façon. Cela dépend vraiment de ce genre d'objet v est dans votre code d'exemple. Si le type de v est un conteneur standard de type, comme vector<T> v; alors qu'il n'a pas d'importance si vous utilisez la gratuit ou des fonctions membres, ils font la même chose. Si votre objet v est plus générique, comme dans le code suivant:

template <class T>
void foo(T& v) {
  auto i = v.begin();     
  auto e = v.end(); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

Ensuite, en utilisant les fonctions de membre des sauts de votre code pour T = C des tableaux, chaînes C, les énumérations, etc. À l'aide de la non-membre de fonctions, de vous faire de la publicité plus générique de l'interface que les gens peuvent facilement étendre. En utilisant la fonction d'interface:

template <class T>
void foo(T& v) {
  auto i = begin(v);     
  auto e = end(v); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

Le code fonctionne maintenant avec T = C des tableaux et chaînes C. En train d'écrire une petite quantité de code d'adaptateur:

enum class color { RED, GREEN, BLUE };
static color colors[]  = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c)   { return end(colors); }

Nous pouvons obtenir votre code doit être compatible avec l'objet iterable les énumérations trop. Je pense que l'Herbe du point principal est que l'utilisation de la gratuit des fonctions est tout aussi simple que d'utiliser les fonctions de membre, et il donne à votre code de compatibilité descendante avec C des types de séquence et en avant la compatibilité avec les non-stl types de séquence (et futur-types stl!), avec un faible coût à d'autres développeurs.

5voto

Christian Rau Points 29137

Alors que le non-fonctions de membre ne fournit pas de prestations pour les conteneurs standard, à l'aide de leur applique de manière plus cohérente et flexible de style. Si vous avez à un certain temps, veulent étendre existantes non-std classe de conteneur, vous préférez définir les surcharges des fonctions libres, au lieu de modifier la classe de définition. Donc, pour les non-std conteneurs ils sont très utiles et toujours à l'aide de la gratuit des fonctions rend votre code plus souple, que vous pouvez remplacer le conteneur std par un non-std conteneur plus facilement, et le type de conteneur est plus transparent pour votre code comme il prend en charge un éventail beaucoup plus large d'implémentations de conteneur.

Mais bien sûr, cela doit toujours être pondéré correctement et sur l'abstraction n'est pas bon non plus. Bien que l'utilisation de la gratuit des fonctions n'est pas une abstraction, elle a néanmoins des sauts de compatibilité avec le C++03 code, qui, à ce jeune âge du C++11 peut encore être un problème pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X