Pourquoi utiliser des non-membre de début et de fin de fonctions en C++11?
Chaque conteneur standard a un begin
et end
méthode pour le retour des itérateurs pour ce conteneur. Cependant, C++11 a, apparemment, a introduit la gratuité des fonctions appelées std::begin
et std::fin
qui appellent le begin
et end
fonctions de membre. Donc, au lieu d'écrire
auto i = v.begin();
auto e = v.end();
vous voulez écrire
using std::begin;
using std::end;
auto i = begin(v);
auto e = end(v);
Dans son discours, L'Écriture Moderne Du C++, 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. Toutefois, il n'entre pas dans le détail de 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 de std::begin
et std::end
faire au-delà de l'appel de leur membre correspondant de la fonction des versions, et pourquoi voudriez-vous utiliser?
- C'est un de moins de caractère, à l'exception des points pour vos enfants: xkcd.com/297
Vous devez vous connecter pour publier un commentaire.
Comment appelez-vous
.begin()
et.end()
sur un C-ensemble ?Libre-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.
end
sur un C tableau? Ils n'ont pas de sa longueur, une partie d'entre eux.begin
est aussi facile en passant le pointeur, etend
est aussi simple que de passer le pointeur de plus la longueur. Donc,begin
semble inutile, et vous ne pouvez pas avoirend
- à moins queend
pouvez prendre une longueur d'elle (Herbe de ne pas donner un tel exemple), dans ce cas, vous êtes à la toujours ne pas obtenir quelque chose de plus juste en passant le pointeur de plus la longueur. Donc,begin
etend
sont toujours inutiles.char*
, vous pouvez regarder pour le\0
personnage, même si c'est plutôt une lente approche par rapport à la mémorisation de la longueur.end
statiquement déclaré tableaux (int foo[5]
) en utilisant le modèle de programmation des tours. Une fois qu'il a pourri à un pointeur, vous êtes hors de la chance.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
etend
sur un C tableau aussi longtemps que vous ne l'avez pas déjà pourri à un pointeur de vous-même - @Huw sorts de sortir. Comme pour pourquoi vous voulez: imaginez que vous refactorisation de code qui a été l'aide d'un tableau à l'utilisation d'un vecteur (ou vice-versa, pour quelque raison que ce soit). Si vous avez été en utilisantbegin
etend
, et peut-être quelques petits malins typedeffing, la mise en œuvre du code de ne pas avoir à changer à tous (sauf peut-être certains des typedefs).end
sur tout C gamme de style, statique ou local. Vous ne pouvez pas l'utiliser sur un pointeur.end()
sont l'une des raisons pour lesquelles le C++ n'a pas les adopter. g++ est en train de faire les utilisateurs du tort à les soutenir en C++.)&arr[0]+sizeof(arr)/sizeof(*arg)
. Addmittedly, mais qui nécessiterait une magie spéciale de traitement d'un std::fin de conflit, mais il pourrait encore être fait. Spécial compilateur magie est requis pour certaines pièces de bibliothèque déjà...end
est possible, mais en déduire le type de tableau n'est pas. Il n'est pas possible pour une fonction pour faire un tableau comme paramètre. Le plus proche (et la seule possibilité de s'inspirer autant que je sache) est d'avoir un modèle en prenant une référence à un tableau (comme indiqué par @Huw), il nécessite toutefois une compilation connue de taille :/template<typename T> T* end(size_t N; T (&a)[N]) {return a+N;}
. Qui serait considérée comme ayant un paramètre dans presque tous égards, bien que dans le code généré, il aurait en fait deux paramètres, avec le compilateur fournissantsizeof(arr)/sizeof(*arr)
sur le site d'appel (sauf si inline bien sûr).error: cannot build range expression with array function parameter 'c' since parameter with array type 'int []' is treated as pointer type 'int *'
Considérer le cas où vous avez de la bibliothèque qui contiennent de la classe:
il dispose de 2 méthodes:
pour effectuer une itération sur les valeurs que vous devez hériter de cette classe et de définir
begin()
etend()
méthodes pour les cas deMais si vous utilisez toujours la
vous pouvez faire ceci:
où
SpecialArrayIterator
est quelque chose comme:maintenant
i
ete
peut être légalement utilisé pour l'itération et d'accéder à des valeurs de SpecialArraytemplate<>
lignes. Vous êtes à la déclaration d'une nouvelle fonction de surcharge, pas de spécialisation d'un modèle.À l'aide de la
begin
etend
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 manquant un
.begin()
méthode). En supposant que vous ne pouvez pas simplement le corriger le conteneur, la meilleure option est de surcharger lebegin
fonction. Herb est ce qui suggère que vous utilisez toujours labegin
fonction de promouvoir l'uniformité et de la cohérence dans votre code. Au lieu d'avoir à se souvenir des conteneurs mode d'accompagnementbegin
et qui ont besoin de la fonctionbegin
.Que d'un côté, 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 tentefoo(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.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 de fonctions (). De
<iterator>
, inclus automatiquement lorsque vous utilisez l'un des conteneurs standard comme<vector>
,<list>
, etc., vous obtenez: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, commevector<T> v;
alors il n'a pas d'importance si vous utilisez la gratuit ou des fonctions membres, ils font la même chose. Si votre objetv
est plus générique, comme dans le code suivant:Puis 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:
Le code fonctionne maintenant avec T = C des tableaux et chaînes C. En train d'écrire une petite quantité de code d'adaptateur:
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.
enum
ou de tout autre type fondamental par référence, bien; ils seront moins coûteux pour copie qu'ils ne le sont indirects de.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.
boost::begin()
/end()
, donc il n'y a pas de réelle incompatibilité 🙂begin/end
). Donc, je considère qu'une incompatibilité de pur C++03, trop. Mais comme dit, il est plutôt de petite taille (et de plus en plus petits) incompatibilité, comme le C++11 (au moinsbegin/end
en particulier) est de plus en plus de l'adoption, de toute façon.Un des avantages de
std::begin
etstd::end
est qu'ils servent de points d'extensionpour la mise en œuvre de la norme d'interface pour des classes externes.
Si vous souhaitez utiliser
CustomContainer
classe avec la gamme à base de boucle ou d'un modèlefonction qui attend
.begin()
et.end()
méthodes, vous feriez bien évidemmentmettre en œuvre ces méthodes.
Si la classe ne fournir ces méthodes, ce n'est pas un problème. Quand ça ne marche pas,
vous auriez à le modifier*.
Ce n'est pas toujours possible, par exemple lors de l'utilisation d'une bibliothèque externe, avec
commerciale et fermé source.
Dans de telles situations,
std::begin
etstd::end
venir dans maniable, puisque l'on peut offriritérateur de l'API sans modifier la classe elle-même, mais plutôt la surcharge de fonctions libres.
Exemple: supposons que vous souhaitez mettre en œuvre
count_if
fonction qui prend un conteneurau lieu d'une paire d'itérateurs. Un tel code pourrait ressembler à ceci:
Maintenant, quelle que soit la classe que vous souhaitez utiliser avec cette coutume
count_if
, vous n'avezpour ajouter gratuitement deux fonctions, au lieu de modifier ces classes.
Maintenant, C++ dispose d'un méchanisme d'identification appelé Argument Dépendante De Recherche
(ADL), ce qui rend cette approche plus souple.
En bref, l'ADL signifie, que lorsqu'un compilateur résout un franc fonction (j'. e.
fonction sans espace de noms, comme
begin
au lieu destd::begin
), il sera égalementconsidérer que les fonctions déclarées dans les espaces de noms de ses arguments. Par exemple:
Dans ce cas, il n'est pas question que les noms qualifiés sont
some_lib::begin
etsome_lib::end
- depuis
CustomContainer
est danssome_lib::
trop, le compilateur va utiliser ces surcharges danscount_if
.C'est également la raison pour avoir
using std::begin;
etusing std::end;
danscount_if
.Cela nous permet d'utiliser sans réserve
begin
etend
, ce qui permet donc à l'ADL etpermettant compilateur de choisir
std::begin
etstd::end
lorsque aucune autre solution n'est trouvée.On peut manger le biscuit et ont le cookie - je. e. avoir un moyen de fournir une implémentation personnalisée
de
begin
/end
tandis que le compilateur peut revenir à des standards.Quelques remarques:
Pour la même raison, il ya d'autres fonctions similaires:
std::rbegin
/rend
,std::size
etstd::data
.Comme d'autres réponses mentionne,
std::
versions ont des surcharges pour nu tableaux. C'est utile,mais est simplement un cas particulier de ce que j'ai décrit ci-dessus.
À l'aide de
std::begin
et les amis est particulièrement bonne idée lors de l'écriture de code de modèle,ce qui rend ces modèles plus génériques. Pour les non-modèle, vous pourriez juste
ainsi les méthodes d'utilisation, le cas échéant.
P. S. je suis conscient que ce post est près de 7 ans. Je suis tombé sur ça parce que je voulais
répondre à une question qui a été marqué comme un doublon et découvert que pas de réponse ici, mentionne l'ADL.