Chaque conteneur standard a une méthode begin
et end
pour retourner des itérateurs pour ce conteneur. Cependant, C ++ 11 a apparemment introduit des fonctions libres appelées std::begin
et std::end
qui appellent les fonctions membres begin
et end
. Donc, au lieu d'écrire
auto i = v.begin();
auto e = v.end();
tu écrirais
auto i = std::begin(v);
auto e = std::end(v);
Dans son exposé, Writing Modern C ++ , Herb Sutter dit que vous devez toujours utiliser les fonctions gratuites maintenant lorsque vous voulez l'itérateur de début ou de fin pour un conteneur. Cependant, il n'entre pas dans les détails de pourquoi vous voudriez. En regardant le code, il vous enregistre tous d'un caractère. Ainsi, en ce qui concerne les conteneurs standard, les fonctions gratuites semblent totalement inutiles. Herb Sutter a indiqué qu'il y avait des avantages pour les conteneurs non standard, mais encore une fois, il n'est pas entré dans les détails.
Donc, la question est de savoir ce que font exactement les versions de fonction libres de std::begin
et std::end
font au-delà d'appeler leurs versions de fonctions membres correspondantes, et pourquoi voudriez-vous les utiliser?
std::
tout le temps.Réponses:
Comment appelez-vous
.begin()
et.end()
sur un C-array?Les fonctions libres permettent une programmation plus générique car elles peuvent être ajoutées par la suite, sur une structure de données que vous ne pouvez pas modifier.
la source
end
pour les tableaux statiquement déclarés (int foo[5]
) en utilisant des astuces de programmation de modèle. Une fois qu'il s'est désintégré en un pointeur, vous n'avez bien sûr pas de chance.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
etend
sur un tableau C tant que vous ne l'avez pas déjà décomposé en pointeur - @Huw le précise. Quant à savoir pourquoi vous voudriez: imaginez que vous avez refactorisé le code qui utilisait un tableau pour utiliser un vecteur (ou vice versa, pour une raison quelconque). Si vous avez utilisébegin
etend
, et peut-être des effets de frappe intelligents, le code d'implémentation n'aura pas à changer du tout (sauf peut-être certains des typedefs).Considérez le cas lorsque vous avez une bibliothèque contenant une classe:
il a 2 méthodes:
pour itérer sur ses valeurs que vous devez hériter de cette classe et définir
begin()
et lesend()
méthodes pour les cas oùMais si vous utilisez toujours
tu peux le faire:
où
SpecialArrayIterator
est quelque chose comme:maintenant
i
ete
peut être légalement utilisé pour l'itération et l'accès aux valeurs de SpecialArrayla source
template<>
lignes. Vous déclarez une nouvelle surcharge de fonction, sans spécialiser un modèle.L'utilisation des fonctions
begin
etend
free ajoute une couche d'indirection. Habituellement, cela est fait pour permettre plus de flexibilité.Dans ce cas, je peux penser à quelques utilisations.
L'utilisation la plus évidente est pour les tableaux C (pas les pointeurs c).
Une autre consiste à essayer d'utiliser un algorithme standard sur un conteneur non conforme (c'est-à-dire qu'il manque une
.begin()
méthode au conteneur ). En supposant que vous ne pouvez pas simplement réparer le conteneur, la meilleure option suivante consiste à surcharger labegin
fonction. Herb vous suggère d'utiliser toujours labegin
fonction pour promouvoir l'uniformité et la cohérence de votre code. Au lieu de devoir vous rappeler quels conteneurs prennent en charge la méthodebegin
et lesquels nécessitent une fonctionbegin
.En aparté, le prochain C de la tour devrait copier de D' notation pseudo-membre . Si
a.foo(b,c,d)
n'est pas défini, il essaie à la placefoo(a,b,c,d)
. C'est juste un peu de sucre syntaxique pour nous aider les pauvres humains qui préfèrent le sujet puis l'ordre des verbes.la source
Pour répondre à votre question, les fonctions libres begin () et end () par défaut ne font rien de plus que d'appeler les fonctions membre .begin () et .end () du conteneur. De
<iterator>
, inclus automatiquement lorsque vous utilisez l' un des conteneurs standards comme<vector>
,<list>
, etc., vous obtenez:La deuxième partie de votre question est de savoir pourquoi préférer les fonctions gratuites si elles ne font qu'appeler les fonctions membres de toute façon. Cela dépend vraiment du type d'objet
v
dans votre exemple de code. Si le type de v est un type de conteneur standard, commevector<T> v;
alors peu importe si vous utilisez les fonctions libres ou membres, ils font la même chose. Si votre objetv
est plus générique, comme dans le code suivant:Ensuite, l'utilisation des fonctions membres rompt votre code pour les tableaux T = C, les chaînes C, les énumérations, etc. En utilisant les fonctions non membres, vous annoncez une interface plus générique que les utilisateurs peuvent facilement étendre. En utilisant l'interface de fonction gratuite:
Le code fonctionne désormais avec les tableaux T = C et les chaînes C. Écrivant maintenant une petite quantité de code d'adaptateur:
Nous pouvons également rendre votre code compatible avec les énumérations itérables. Je pense que le principal point de Herb est que l'utilisation des fonctions gratuites est tout aussi simple que l'utilisation des fonctions membres, et cela donne à votre code une compatibilité descendante avec les types de séquence C et une compatibilité ascendante avec les types de séquence non stl (et les types future-stl!), à faible coût pour les autres développeurs.
la source
enum
ou tout autre type fondamental par référence, cependant; ils seront moins chers à copier qu'à indirectement.Un avantage de
std::begin
etstd::end
est qu'ils servent de points d'extension pour implémenter une interface standard pour les classes externes.Si vous souhaitez utiliser une
CustomContainer
classe avec une fonction de boucle ou de modèle basée sur une plage qui attend.begin()
et des.end()
méthodes, vous devrez évidemment implémenter ces méthodes.Si la classe fournit ces méthodes, ce n'est pas un problème. Dans le cas contraire, vous devrez le modifier *.
Cela n'est pas toujours possible, par exemple lors de l'utilisation d'une bibliothèque externe, en particulier une source commerciale et fermée.
Dans de telles situations,
std::begin
etstd::end
utile, car on peut fournir une API d'itérateur sans modifier la classe elle-même, mais plutôt surcharger les fonctions libres.Exemple: supposons que vous souhaitiez implémenter une
count_if
fonction qui prend un conteneur au lieu d'une paire d'itérateurs. Un tel code pourrait ressembler à ceci:Maintenant, pour n'importe quelle classe que vous souhaitez utiliser avec cette coutume
count_if
, vous n'avez qu'à ajouter deux fonctions gratuites, au lieu de modifier ces classes.Maintenant, C ++ a un mécanisme appelé ADL ( Argument Dependent Lookup ), qui rend cette approche encore plus flexible.
En bref, ADL signifie que lorsqu'un compilateur résout une fonction non qualifiée (c'est-à-dire une fonction sans espace de noms, comme
begin
au lieu destd::begin
), il considérera également les fonctions déclarées dans les espaces de noms de ses arguments. Par exemple:Dans ce cas, peu importe que les noms qualifiés soient
some_lib::begin
etsome_lib::end
- puisqueCustomContainer
c'estsome_lib::
aussi le cas, le compilateur utilisera ces surcharges danscount_if
.C'est aussi la raison d'avoir
using std::begin;
etusing std::end;
de participercount_if
. Cela nous permet d'utiliser sans réservebegin
etend
, par conséquent, de permettre ADL et de permettre au compilateur de choisirstd::begin
etstd::end
quand aucune autre alternative n'est trouvée.Nous pouvons manger le cookie et l'avoir - c'est-à-dire avoir un moyen de fournir une implémentation personnalisée de
begin
/end
tandis que le compilateur peut revenir à ceux standard.Quelques notes:
Pour la même raison, il existe d'autres fonctions similaires:
std::rbegin
/rend
,std::size
etstd::data
.Comme d'autres réponses le mentionnent, les
std::
versions ont des surcharges pour les tableaux nus. C'est utile, mais c'est simplement un cas particulier de ce que j'ai décrit ci-dessus.L'utilisation de
std::begin
et d'amis est particulièrement bonne lors de l'écriture de code de modèle, car cela rend ces modèles plus génériques. Pour les non-modèles, vous pourriez tout aussi bien utiliser des méthodes, le cas échéant.PS Je sais que ce post a presque 7 ans. Je suis tombé dessus parce que je voulais répondre à une question marquée comme doublon et j'ai découvert qu'aucune réponse ne mentionne ADL.
la source
Alors que les fonctions non membres n'offrent aucun avantage aux conteneurs standard, leur utilisation renforce un style plus cohérent et plus flexible. Si vous souhaitez à un moment donné étendre une classe de conteneur non std existante, vous préférez définir des surcharges des fonctions libres, au lieu de modifier la définition de la classe existante. Donc, pour les conteneurs non std, ils sont très utiles et toujours en utilisant les fonctions gratuites rend votre code plus flexible en ce que vous pouvez remplacer le conteneur std par un conteneur non std plus facilement et le type de conteneur sous-jacent est plus transparent pour votre code car il prend en charge une plus grande variété d'implémentations de conteneurs.
Mais bien sûr, cela doit toujours être pondéré correctement et la surabstraction n'est pas bonne non plus. Bien que l'utilisation des fonctions gratuites ne soit pas une sur-abstraction, elle rompt néanmoins la compatibilité avec le code C ++ 03, qui à ce jeune âge de C ++ 11 pourrait toujours être un problème pour vous.
la source
boost::begin()
/end()
, donc il n'y a pas de réelle incompatibilité :)begin/end
). Je considérerais donc cela également comme une incompatibilité avec le C ++ 03 pur. Mais comme dit, il s'agit d'une incompatibilité plutôt petite (et qui diminue), car C ++ 11 (au moinsbegin/end
en particulier) est de plus en plus adopté, de toute façon.En fin de compte, l'avantage est dans le code qui est généralisé de sorte qu'il soit indépendant du conteneur. Il peut fonctionner sur un
std::vector
, un tableau ou une plage sans modification du code lui-même.De plus, des conteneurs, même des conteneurs n'appartenant pas à un propriétaire, peuvent être mis à niveau de telle sorte qu'ils peuvent également être utilisés de manière agnostique par code à l'aide d'accesseurs basés sur des plages non membres.
Voir ici pour plus de détails.
la source