311 votes

Comment faire pour que mon type personnalisé pour travailler avec "basés sur la plage pour les boucles"?

Comme beaucoup de gens ces jours-ci j'ai essayé les différentes fonctionnalités que C+11 apporte. L'un de mes favoris est la "gamme à base de boucles".

Je comprends que:

for(Type& v : a) { ... }

Est équivalent à:

for(auto iv = begin(a); iv != end(a); ++iv)
{
  Type& v = *iv;
  ...
}

Et qu' begin() simplement les retours a.begin() pour les conteneurs standard.

Mais que faire si je veux faire mon type personnalisé "de la gamme, à base de boucle"-conscient ?

Dois-je simplement se spécialisent begin() et end() ?

Si mon type personnalisé appartient à l'espace de noms xml, dois-je définir xml::begin() ou std::begin() ?

En bref, quelles sont les lignes directrices pour le faire ?

54voto

Steve Jessop Points 166970

La partie pertinente de la norme est 6.5.4/1:

si _RangeT est un type de classe, le unqualified-id de début et de fin sont recherché dans le champ d'application de classe _RangeT comme si par un membre de la classe d'accès recherche (3.4.5), et si l'une (ou les deux) trouve au moins une déclaration, - expr et de fin expr sont __range.begin() et __range.end(), respectivement;

- dans le cas contraire, commencez-expr et de fin expr sont begin(__range)et end(__range), respectivement, où de début et de fin sont considérés avec argument dépendant de recherche (3.4.2). Pour les fins de ce nom recherche, de l'espace de noms std est associé à un espace de noms.

Ainsi, vous pouvez effectuer l'une des opérations suivantes:

  • définir begin et end des fonctions de membre du
  • définir begin et end gratuit fonctions qui seront trouvés par ADL (version simplifiée: les mettre dans le même espace de noms que celui de la classe)
  • se spécialiser std::begin et std::end

std::begin des appels à l' begin() fonction de membre de toute façon, donc si vous ne mettez en œuvre l'un des ci-dessus, le résultat devrait être le même, peu importe celle que vous choisissez. C'est les mêmes résultats ont varié à base de boucles, et aussi le même résultat pour le simple mortel code qui ne dispose pas de son propre magique de résolution de nom de règles de juste n' using std::begin; , suivi par un appel non qualifié pour begin(a).

Si vous implémentez les fonctions de membre et de l'ADL fonctions, cependant, puis de la gamme, à base de boucles doivent appeler les fonctions de membre, alors que de simples mortels va appeler l'ADL fonctions. Meilleur assurez-vous qu'ils font la même chose dans ce cas!

Si la chose que vous avez écrit, met en œuvre le conteneur de l'interface, alors il va falloir begin() et end() fonctions de membre déjà, ce qui devrait être suffisant. Si c'est une plage qui n'est pas un conteneur (ce qui serait une bonne idée si c'est immuable, ou si vous ne connaissez pas la taille à l'avant), vous êtes libre de choisir.

Les options que vous présentez, notez que vous ne devez pas surcharger std::begin(). Vous êtes autorisé à se spécialiser modèles standard pour un type défini par l'utilisateur, mais à côté de cela, l'ajout de définitions de l'espace de noms std est un comportement indéfini. Mais de toute façon, spécialisée fonctions standard est un mauvais choix, si seulement en raison de l'absence de fonction partielle de spécialisation signifie que vous ne pouvez le faire pour une seule classe, et pas pour un modèle de classe.

39voto

BЈовић Points 28674

Dois-je simplement se spécialisent begin() et end() ?

Pour autant que je sais, c'est assez. Vous devez également vous assurer que l'incrémentation du pointeur de le le commencer à la fin.

L'exemple suivant (il manque const version de début et de fin) compile et fonctionne très bien.

#include <iostream>
#include <algorithm>

int i=0;

struct A
{
    A()
    {
        std::generate(&v[0], &v[10], [&i](){  return ++i;} );
    }
    int * begin()
    {
        return &v[0];
    }
    int * end()
    {
        return &v[10];
    }

    int v[10];
};

int main()
{
    A a;
    for( auto it : a )
    {
        std::cout << it << std::endl;
    }
}

Voici un autre exemple avec début/fin comme des fonctions. Ils doivent être dans le même espace de nom que la classe, en raison de l'ADL :

#include <iostream>
#include <algorithm>


namespace foo{
int i=0;

struct A
{
    A()
    {
        std::generate(&v[0], &v[10], [&i](){  return ++i;} );
    }

    int v[10];
};

int *begin( A &v )
{
    return &v.v[0];
}
int *end( A &v )
{
    return &v.v[10];
}
} // namespace foo

int main()
{
    foo::A a;
    for( auto it : a )
    {
        std::cout << it << std::endl;
    }
}

15voto

Chris Redford Points 1417

Dans le cas où vous souhaitez sauvegarder une classe itération directement avec ses std::vector ou std::map membre, voici le code:

#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <map>
using std::map;


/////////////////////////////////////////////////////
/// classes
/////////////////////////////////////////////////////

class VectorValues {
private:
    vector<int> v = vector<int>(10);

public:
    vector<int>::iterator begin(){
        return v.begin();
    }
    vector<int>::iterator end(){
        return v.end();
    }
    vector<int>::const_iterator begin() const {
        return v.begin();
    }
    vector<int>::const_iterator end() const {
        return v.end();
    }
};

class MapValues {
private:
    map<string,int> v;

public:
    map<string,int>::iterator begin(){
        return v.begin();
    }
    map<string,int>::iterator end(){
        return v.end();
    }
    map<string,int>::const_iterator begin() const {
        return v.begin();
    }
    map<string,int>::const_iterator end() const {
        return v.end();
    }

    const int& operator[](string key) const {
        return v.at(key);
    }
    int& operator[](string key) {
        return v[key];
    } 
};


/////////////////////////////////////////////////////
/// main
/////////////////////////////////////////////////////

int main() {
    // VectorValues
    VectorValues items;
    int i = 0;
    for(int& item : items) {
        item = i;
        i++;
    }
    for(int& item : items)
        cout << item << " ";
    cout << endl << endl;

    // MapValues
    MapValues m;
    m["a"] = 1;
    m["b"] = 2;
    m["c"] = 3;
    for(auto pair: m)
        cout << pair.first << " " << pair.second << endl;
}

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