Ou est-il sûr d'utiliser le vecteur si l'énumérateur de T ne fait que lister tous les éléments ?
Réponses
Trop de publicités?Il n'est pas nécessaire en C++, et voici pourquoi :
C# ne supporte que le polymorphisme dynamique. Ainsi, pour créer un algorithme réutilisable, vous avez besoin d'une interface que tous les itérateurs mettront en œuvre. Il s'agit de IEnumerator<T>
et IEnumerable<T>
est une usine pour retourner un itérateur.
Les modèles C++, en revanche, prennent en charge le typage des canards. Cela signifie que vous n'avez pas besoin de contraindre un paramètre de type générique par une interface afin d'accéder aux membres -- le compilateur recherchera les membres par leur nom pour chaque instanciation individuelle du modèle.
Les conteneurs et itérateurs C++ ont des interfaces implicites, ce qui est équivalent à .NET IEnumerable<T>
, IEnumerator<T>
, ICollection<T>
, IList<T>
à savoir :
Pour les conteneurs :
-
iterator
yconst_iterator
typedefs -
begin()
fonction de membre - répond au besoin deIEnumerable<T>::GetEnumerator()
-
end()
fonction membre -- au lieu deIEnumerator<T>::MoveNext()
valeur de retour
Pour les itérateurs avant :
-
value_type
typedef -
operator++
-- au lieu deIEnumerator<T>::MoveNext()
-
operator*
yoperator->
-- au lieu deIEnumerator<T>::Current
- type de retour de référence de
operator*
-- au lieu deIList<T>
indexeur setter -
operator==
yoperator!=
-- Il n'y a pas d'équivalent réel en .NET, mais avec le conteneurend()
correspond àIEnumerator<T>::MoveNext()
valeur de retour
Pour les itérateurs à accès aléatoire :
-
operator+
,operator-
,operator[]
-- au lieu deIList<T>
Si vous les définissez, les algorithmes standard fonctionneront avec votre conteneur et votre itérateur. Aucune interface n'est nécessaire, aucune fonction virtuelle n'est nécessaire. Le fait de ne pas utiliser de fonctions virtuelles rend le code générique C++ plus rapide que le code .NET équivalent, parfois beaucoup plus rapide.
Remarque : lors de l'écriture d'algorithmes génériques, il est préférable d'utiliser la fonction std::begin(container)
y std::end(container)
au lieu des fonctions membres du conteneur. Cela permet à votre algorithme d'être utilisé avec des tableaux bruts (qui n'ont pas de fonctions membres) en plus des conteneurs STL. Les tableaux bruts et les pointeurs bruts répondent à toutes les autres exigences des conteneurs et des itérateurs, à cette seule exception près.
Si nous nous en tenons strictement à la question, la réponse est non, pour autant que je sache. Les gens n'ont pas cessé de répondre en indiquant quel est le substitut disponible en C++, ce qui peut être une bonne information mais pas une réponse, et ce que le PO savait très probablement déjà.
Je ne suis pas du tout d'accord pour dire que "ce n'est pas nécessaire", c'est juste que la conception des bibliothèques standard C++ et .NET est différente. La principale caractéristique de IEnumerable<> est qu'il est polymorphe, et qu'il permet donc à l'appelant d'utiliser la classe qu'il veut (array, List, Set etc.), tout en assurant un typage fort à la compilation, sans risque même dans les API des bibliothèques.
La seule alternative en C++ est les templates. Mais les modèles C++ ne sont pas des génériques d'exécution typés de manière sûre, ce sont en fait des sortes de macros. Donc, tout d'abord, avec les modèles en C++, vous êtes obligé de fournir le code source complet du modèle à quiconque a besoin d'utiliser votre modèle. De plus, si vous faites de l'API de votre bibliothèque un modèle, vous perdez la possibilité de garantir qu'un appel à celui-ci sera compilé, et le code n'est pas automatiquement auto-documenté.
Je compatis pleinement avec tout autre programmeur qui utilise à la fois C# et C++ et qui est frustré par ce point.
Cependant, il est prévu que C++2X ajoute des fonctionnalités telles que les intervalles (ce qui pourrait satisfaire le PO ?); ainsi que des concepts (qui traitent de la vérification de type faible/mauvaise des modèles -- faille admis par Bjarne Stroustrup lui-même), et les modules (qui peuvent ou non aider à réduire la douleur des modèles d'en-tête seulement).
La méthode standard du C++ consiste à passer deux itérateurs :
template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
for (; begin != end; ++begin)
{
do_something_with(*begin);
}
}
Exemple de code client :
std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(vec.begin(), vec.end());
std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(lst.begin(), lst.end());
int arr[] = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(arr + 0, arr + 8);
Yay la programmation générique !
IEnumerable<T>
est conceptuellement très différent de vector
.
El IEnumerable
fournit à l'avance , en lecture seule l'accès à une séquence d'objets, quel que soit le conteneur (le cas échéant) qui contient les objets. A vector
est en fait un conteneur lui-même.
En C++, si vous voulez donner accès à un conteneur sans donner les détails de ce conteneur, la convention est de passer dans deux itérateurs représentant le début et la fin du conteneur.
Un bon exemple est la définition de la STL C++ de la fonction accumuler que l'on peut opposer à IEnumerable<T>. Agrégat
En C++
int GetProduct(const vector<int>& v)
{
// We don't provide the container, but two iterators
return std::accumulate(v.begin(), v.end(), 1, multiplies<int>());
}
En C#
int GetProduct(IEnumerable<int> v)
{
v.Aggregate(1, (l, r) => l*r);
}