Il y a beaucoup de fonctions utiles dans <algorithm>
, mais toutes fonctionnent sur des "séquences" - des paires d'itérateurs. Par exemple, si j’ai un conteneur et que j’aime courir std::accumulate
dessus, j’ai besoin d’écrire:
std::vector<int> myContainer = ...;
int sum = std::accumulate(myContainer.begin(), myContainer.end(), 0);
Quand tout ce que je compte faire, c'est:
int sum = std::accumulate(myContainer, 0);
Ce qui est un peu plus lisible et plus clair, à mes yeux.
Maintenant, je peux voir que dans certains cas, vous voudrez peut-être n’utiliser que des parties d’un conteneur. Il est donc certainement utile d’avoir la possibilité de passer des plages. Mais au moins d'après mon expérience, c'est un cas spécial rare. Je veux généralement opérer sur des conteneurs entiers.
Il est facile d'écrire une fonction d'enveloppe qui prend un récipient et des appels begin()
et end()
à ce sujet , mais ces fonctions pratiques ne sont pas inclus dans la bibliothèque standard.
J'aimerais connaître le raisonnement derrière ce choix de design STL.
la source
boost::accumulate
Réponses:
Selon votre expérience , il s’agit peut-être d’un cas rare , mais en réalité, le conteneur dans son ensemble constitue le cas particulier et la plage arbitraire le cas général.
Vous avez déjà remarqué que vous pouvez implémenter l' intégralité du conteneur en utilisant l'interface actuelle, mais vous ne pouvez pas faire l'inverse.
Ainsi, le bibliothécaire-rédacteur avait le choix entre implémenter deux interfaces à l’avance ou n’en implémenter qu’une seule qui couvre toujours tous les cas.
C'est vrai, d'autant plus que les fonctions libres
std::begin
etstd::end
sont maintenant incluses.Donc, disons que la bibliothèque fournit la surcharge de commodité:
maintenant, il doit également fournir la surcharge équivalente en prenant un foncteur de comparaison, et nous devons fournir les équivalents pour tout autre algorithme.
Mais nous avons au moins couvert tous les cas où nous souhaitons opérer avec un conteneur complet, non? Pas tout à fait. Considérer
Si nous voulons gérer les opérations en arrière sur les conteneurs, nous avons besoin d' une autre méthode (ou d'une paire de méthodes) par algorithme existant.
L’approche basée sur la plage est donc plus générale dans le sens simple qui suit:
Bien sûr, il existe une autre raison valable, à savoir que la standardisation du STL nécessitait déjà beaucoup de travail et que le gonfler avec des enveloppes de commodité avant qu'il ne soit largement utilisé ne serait pas une grande utilisation du temps limité des comités. Si vous êtes intéressé, vous trouverez le rapport technique de Stepanov & Lee ici
Comme mentionné dans les commentaires, Boost.Range fournit une approche plus récente sans nécessiter de modification de la norme.
la source
f(c.begin(), c.end(), ...)
-le à la surcharge la plus couramment utilisée (quelle que soit votre détermination) pour éviter de doubler le nombre de surcharges. De plus, les adaptateurs d'itérateur sont complètement orthogonaux (comme vous le constatez, ils fonctionnent bien en Python, dont les itérateurs fonctionnent très différemment et qui n'ont pas le pouvoir dont vous parlez).std::sort(std::range(start, stop))
.#define MAKE_RANGE(container) (container).begin(), (container).end()
</ jk>Il s'avère qu'il y a un article de Herb Sutter sur ce sujet même. Fondamentalement, le problème est l’ambiguïté de surcharge. Compte tenu de ce qui suit:
Et en ajoutant ce qui suit:
Cela rendra difficile à distinguer
4
et1
correctement.Les concepts, tels que proposés mais finalement pas inclus dans C ++ 0x, auraient résolu ce problème, et il est également possible de les contourner à l'aide de
enable_if
. Pour certains algorithmes, cela ne pose aucun problème. Mais ils ont décidé de ne pas le faire.Maintenant, après avoir lu tous les commentaires et réponses ici, je pense que les
range
objets seraient la meilleure solution. Je pense que je vais regarderBoost.Range
.la source
typename Iter
semble être trop typé pour un langage strict. Je préférerais par exempletemplate<typename Container> void sort(typename Container::iterator, typename Container::iterator); // 1
ettemplate<template<class> Container, typename T> void sort( Container<T>&, std::function<bool(const T&)> ); // 4
etc. (ce qui résoudrait peut-être le problème de l'ambiguïté)T[]::iterator
. En outre, un itérateur approprié n'est pas obligé d'être un type imbriqué d'une collection, il suffit de le définirstd::iterator_traits
.Fondamentalement, une décision héritée. Le concept d'itérateur est calqué sur les pointeurs, mais les conteneurs ne sont pas calqués sur des tableaux. De plus, comme les tableaux sont difficiles à transmettre (en général, il leur faut un paramètre de modèle non typé pour la longueur), très souvent, une fonction ne dispose que de pointeurs.
Mais oui, avec le recul, la décision est fausse. Nous aurions mieux fait d'utiliser un objet range pouvant être construit à partir de
begin/end
oubegin/length
; nous avons maintenant plusieurs_n
algorithmes suffixés.la source
Leur ajout ne vous apporterait aucun pouvoir (vous pouvez déjà traiter tout le conteneur en appelant vous
.begin()
-.end()
même), et cela ajouterait une dernière chose à la bibliothèque qui doit être correctement spécifiée, ajoutée aux bibliothèques par les fournisseurs, testée, maintenue. etc.En bref, ce n'est probablement pas là car il ne vaut pas la peine de conserver un ensemble de modèles supplémentaires simplement pour éviter aux utilisateurs de conteneur entier de saisir un paramètre d'appel de fonction supplémentaire.
la source
std::getline
, et pourtant, c'est dans la bibliothèque. On pourrait même aller jusqu'à dire que les structures de contrôle étendues ne me permettent pas de gagner du pouvoir, car je peux tout faire en utilisant seulementif
etgoto
. Ouais, comparaison injuste, je sais;) Je pense que je peux comprendre le fardeau de la spécification / mise en œuvre / maintenance d'une manière ou d'une autre, mais nous ne parlons ici que d'une petite enveloppe, alors ..A ce jour, http://en.wikipedia.org/wiki/C++11#Range-based_for_loop est une bonne alternative à
std::for_each
. Observez, pas d'itérateurs explicites:(Inspiré par https://stackoverflow.com/a/694534/2097284 .)
la source
<algorithm>
, pas tous les algues qui en ont besoinbegin
et lesend
itérateurs - mais le bénéfice ne peut pas être surestimé! Quand j'ai essayé pour la première fois le C ++ 03 en 2009, je me suis écarté des itérateurs à cause du passe-partout de la boucle, et heureusement ou non, mes projets de l'époque l'ont permis. En redémarrant sur C ++ 11 en 2014, ce fut une mise à niveau incroyable, le langage que C ++ aurait toujours dû être, et je ne peux plus vivre sansauto &it: them
:)