47 votes

Pourquoi dois-je toujours spécifier explicitement la plage dans les fonctions algorithmiques de la STL, même si je veux travailler sur l'ensemble du conteneur ?

Lorsque vous utilisez les fonctions de la STL comme sort() ou min_element() Je dois toujours spécifier la plage par le début et la fin explicitement :

void range_example()
{
    std::vector<int> list = {7, 3, 9, 1, 5, 2};
    auto found_element = std::min_element(list.begin(), list.end());
    std::cout << *found_element << std::endl;
}

Cela a du sens si j'ai l'intention de ne travailler que sur une partie de mon conteneur, mais le plus souvent j'ai besoin que les fonctions travaillent sur l'ensemble du conteneur. Y a-t-il une raison pour laquelle il n'existe pas une fonction surchargée qui permette cela ?

std::vector<int> list = {7, 3, 9, 1, 5, 2};
auto found_element = std::min_element(list);

Existe-t-il un moyen de réaliser un appel de fonction pour l'étendue totale d'un conteneur que j'ai négligé ?

EDIT : Je suis conscient que je peux encapsuler cela dans une fonction moi-même, mais comme cela doit être fait pour toutes les fonctions, j'aimerais éviter cela s'il existe un meilleur moyen.

43voto

Angew Points 53063

La plupart du temps, la bibliothèque standard est conçue pour fournir l'interface minimale nécessaire à l'accomplissement de toutes les tâches requises, c'est-à-dire qu'elle essaie d'éviter le gonflement des interfaces. Vous pouvez opérer sur un conteneur entier lorsque l'algorithme accepte une paire d'itérateurs, mais vous ne pourriez pas opérer sur une sous-gamme si l'algorithme acceptait un conteneur. La paire d'itérateurs est donc plus fondamentale, et c'est ce que fournit la bibliothèque standard. Les fonctions de commodité ne sont généralement pas incluses.

Cependant, vous n'êtes certainement pas la première personne à penser de cette façon, et il y a tout le Gamme Boost. consacrée au traitement d'une plage (à la fois un conteneur et une plage arbitraire) comme une entité unique au lieu d'une paire d'itérateurs.

Il existe également une proposition formelle visant à intégrer La bibliothèque de gammes d'Eric Niebler dans une future version de la bibliothèque standard C++.

7voto

Maksim Solovjov Points 1309

En effet, les algorithmes STL sont indépendants des conteneurs. Les itérateurs leur fournissent un moyen uniforme de travailler, la seule limite étant les garanties que cet algorithme exige de ces itérateurs.

Par exemple, si vous voulez faire une recherche linéaire de min_element() vous n'avez besoin que d'itérateurs de type "forward" (c'est-à-dire qu'ils ne doivent prendre en charge que les éléments suivants operator++ ). Ainsi, vous pouvez écrire une implémentation simple et modélisée qui fonctionnera essentiellement avec tous les conteneurs, malgré la façon dont le conteneur est implémenté sous le capot.

Vous pourriez surcharger les fonctions pour ne prendre que le conteneur et appliquer begin() et end() sur eux, mais cela signifierait que vous avez une interface de plus à retenir.

Modifier

Je suppose que d'autres arguments pourraient être avancés. Puisque la STL a été conçue pour la beauté mathématique et l'accent mis sur le fait que les algorithmes sont séparés des conteneurs, le fait de toujours passer les itérateurs renforcerait cette notion.

D'autre part, en ce qui concerne le langage C++ dans son ensemble, l'un des principaux objectifs de Stroustrup était d'éduquer les développeurs. La pleine puissance des algorithmes STL provient de la possibilité de passer des plages d'itérateurs arbitraires, mais la plupart du temps, vous voulez opérer sur l'ensemble du conteneur. Si vous fournissiez des surcharges pour l'ensemble du conteneur, on pourrait faire valoir qu'un grand nombre de personnes ne prendraient jamais la peine d'apprendre à utiliser les versions de plage, car ce serait précisément ces versions qui tomberaient dans la catégorie "une autre interface à retenir".

6voto

Yakk Points 31636

La raison pratique pour laquelle les surcharges de conteneurs ou de gammes n'ont pas encore été réalisées est liée à la proposition de concepts.

Actuellement, les algorithmes prennent un ensemble de paramètres de modèles et leur imposent des exigences. Si vous passez des types qui ne correspondent pas aux exigences, ils peuvent échouer à compiler ou simplement ne pas fonctionner correctement.

Les surcharges impliquent presque toujours simplement un nombre différent de paramètres.

Si nous devions ajouter des surcharges de conteneurs/plages, nous devrions soit leur donner de nouveaux noms (ick), soit modifier les algorithmes existants pour qu'ils soient adaptés aux surcharges. Une surcharge (itérateur, itérateur, valeur) et une surcharge (gamme, valeur, fonction) ont le même nombre d'arguments, et celle qui est appelée pourrait facilement devenir confuse pour le compilateur (et des résultats inattendus pourraient se produire).

Nous pourrions spécifier les contraintes de surcharge pour tous les algorithmes existants, un par un, puis ajouter les surcharges pour les plages, mais à ce stade, le code et les exigences seraient affreux. Une fois que les concepts auront été ajoutés au langage, nous aurons, nous l'espérons, un ensemble de concepts concis qui décriront ce que les paramètres doivent être, et une fonctionnalité du langage qui rendra l'implémentation facile et propre.

Il se peut que ces algorithmes ne soient pas, en pratique, des surcharges des algorithmes existants, pour des raisons de compatibilité ou autres, mais même cela sera plus facile à mettre au point.

À l'origine, les itérateurs étaient suffisants, et ils découplent les conteneurs des algorithmes. Les intervalles auraient pu être ajoutés à l'époque, mais les mécanismes du langage pour une interprétation propre des intervalles des conteneurs faisaient quelque peu défaut (decltype, par exemple, est utile), et ce n'était pas strictement nécessaire. Depuis, la prise en charge des intervalles a été souhaitée, mais il n'est pas facile de le faire proprement, et il y a (à l'horizon) une extension du langage qui rendra cela beaucoup plus propre et plus facile.

4voto

BeyelerStudios Points 688

Vous pourriez mettre en place votre propre système :

template<class Container>
typename Container::iterator min_element(Container& c) {
    using std::begin;
    using std::end;
    return std::min_element(begin(c), end(c));
}

std::vector<int> list = {7, 3, 9, 1, 5, 2};
auto found_element = min_element(list);

Pour être complet :

template<class Container>
typename std::conditional<
  std::is_const<Container>::value,
  typename Container::const_iterator,
  typename Container::iterator>::type min_element(Container& c) {
    using std::begin;
    using std::end;
    return std::min_element(begin(c), end(c));
}

et pour supporter les tableaux :

template<typename T, size_t N>
T* min_element(T (&arr)[N]) { return std::min_element(arr, arr + N); }

-1voto

Vlad from Moscow Points 36219

Il est facile de définir soi-même une telle fonction. Par exemple

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

template <class T>
decltype( auto ) min_element( T &c )
{
    return std::min_element( std::begin( c ), std::end( c ) );
}

int main()
{
    int a[] = { 5, 7, 3, 1, 9, 6 };

    std::cout << *min_element( a ) << std::endl;

    std::vector<int> v( std::begin( a ), std::end( a ) );

    std::cout << *min_element( v ) << std::endl;
}    

Le résultat du programme est

1
1

J'ai fait une telle suggestion pour les algorithmes std::sort et std::reverse . Vous pouvez en prendre connaissance sur mon forum personnel que je soutiens comme ma page internet personnelle. ici

Bien qu'il soit écrit en russe, vous pouvez le traduire par exemple avec Bing ou google.

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