2010-11-12 35 views
5

J'ai une classe Foo qui contient un map et fournit begin() et end() fonctions itérer dessus:Comment adapter un itérateur défini pour qu'il se comporte comme un itérateur de carte?

class Foo { 
    typedef std::map<int, double> Container; 
    typedef Container::const_iterator const_iterator; 
    Container c_; 
public: 
    const_iterator begin() const { return c_.begin(); } 
    const_iterator end() const { return c_.end(); } 
    void insert(int i, double d) { c_[i] = d; } 
    // ... 

}; 

Maintenant, je voudrais changer intérieurement de std::map<int, double> juste un std::set<int>, mais je ne veux pas pour casser n'importe quel code client.

Donc le double d dans la fonction insert serait maintenant simplement ignoré. Et le code suivant doit encore être valide, où it->second sera maintenant juste toujours 0.0:

Foo foo; 
for(Foo::const_iterator it = foo.begin(); it != foo.end(); ++it) { 
    std::cout << it->first << " " << it->second << std::endl; 
} 

Comment puis-je faire ces changements dans la classe Foo?

En d'autres termes, comment puis-je fournir un Foo::const_iterator qui adapte le nouveau std::set<int>::const_iterator interne pour qu'il se comporte comme l'ancien std::map<int,double>::const_iterator?

MISE À JOUR: La raison pour laquelle je veux me débarrasser du map est l'efficacité de la mémoire. J'ai des millions de Foo instances et ne peut pas se permettre de stocker les valeurs double en eux.

+3

Pour être honnête, cela semble être une très mauvaise idée. Garder le contrat compatible lorsque la sémantique a complètement changé n'est pas significatif. –

+1

@KonradRudolph C++ fait cela depuis le premier jour. – wilhelmtell

+0

@wilhelm: C'est vrai. Mais pas une raison de faire la même chose. –

Répondre

0

Peut-être quelque chose le long des lignes de

operator int()(const std::pair<int, double>& p) const { 
    return p.first; 
} 

peut-être à l'intérieur une enveloppe?

2

utilisant Would

std::set<std::pair<int, double> > 

ne pas être suffisant pour cette comparabilité? A défaut, vous pouvez toujours écrire votre propre itérateur qui enveloppe l'itérateur std :: list et fournit les membres first et second. Fondamentalement, votre opérateur ++ appelle l'opérateur ++ sur l'itérateur réel, etc. et l'opérateur de référence peut renvoyer soit une paire std :: temporaire (par valeur) soit une référence à une paire std :: qui vit dans l'itérateur lui-même (si votre héritage le code peut gérer cela).

mise à jour, par exemple légèrement artificiel, pourrait fonctionner selon votre scénario:

#include <iostream> 
#include <set> 

class Foo { 
    typedef std::set<int> Container; 
    typedef Container::const_iterator legacy_iterator; 
    Container c_; 

    // legacy iterator doesn't have a virtual destructor (probably?), shouldn't 
    // be a problem for sane usage though 
    class compat_iterator : public legacy_iterator { 
    public: 
    compat_iterator(const legacy_iterator& it) : legacy_iterator(it) { 
    } 

    const std::pair<int,double> *operator->() const { 
     static std::pair<int,double> value; 
     value = std::make_pair(**this, 0.0); 
     // Not meeting the usual semantics! 
     return &value; 
    } 
    }; 
public: 
    typedef compat_iterator const_iterator; 

    const_iterator begin() const { return c_.begin(); } 
    const_iterator end() const { return c_.end(); } 

}; 



int main() { 

    Foo foo; 
    for(Foo::const_iterator it = foo.begin(); it != foo.end(); ++it) { 
    std::cout << it->first << " " << it->second << std::endl; 
    } 

} 
+0

Non, j'aurais dû dire ceci: La raison pour laquelle j'ai besoin d'un 'set' maintenant, c'est que je veux économiser de la mémoire. J'ai des millions d'instances 'Foo' et je ne peux plus me permettre de stocker le double. – Frank

+0

J'ai mis à jour ma réponse pour ajouter un exemple de quelque chose qui implémente la compatibilité avec seulement des frais généraux minimes. Bien sûr, prendre l'adresse de l'un ou l'autre des membres de la paire serait une mauvaise idée maintenant, mais si le code hérité ne dépend pas, alors cela pourrait résoudre votre problème. – Flexo

0

Peut-être que vous pouvez définir une classe fake_pair qui implémente first et second et mettre un set<fake_pair> à l'intérieur Foo.

1

Que diriez-vous de quelque chose comme ceci?

#include <iostream> 
#include <map> 
#include <set> 

struct Funky 
{ 
    int first; 
    static const double second; 

    Funky(int i) 
    : first(i) 
    {} 
}; 

const double Funky::second = 0.0; 

bool operator<(const Funky& lhs, const Funky& rhs) 
{ 
    return lhs.first < rhs.first; 
} 

class Foo 
{ 
private: 
    //std::map<int,double> m_data; 
    std::set<Funky> m_data; 
public: 
    //typedef std::map<int,double>::const_iterator const_iterator; 
    typedef std::set<Funky>::const_iterator const_iterator; 

    const_iterator begin() const 
    { 
     return m_data.begin(); 
    } 

    const_iterator end() const 
    { 
     return m_data.end(); 
    } 

    void insert(int i, double d) 
    { 
     //m_data.insert(std::make_pair(i, d)); 
     m_data.insert(i); 
    } 
}; 

int main() 
{ 
    Foo foo; 
    foo.insert(23, 9.0); 
    for(Foo::const_iterator it=foo.begin(), iend=foo.end(); it!=iend; ++it) 
    { 
     std::cout << it->first << ' ' << it->second << '\n'; 
    } 
    return 0; 
} 
+0

Une chose que je devrais souligner est que tout ce qui essaye de changer 'it-> second' échouera maintenant - ainsi vous pourriez vouloir vous débarasser du const. Alternativement, vous pourriez vouloir que les gens sachent ce qui se passe ... –

+0

(Je pense que l'idée de faire ce genre de chose est plutôt mal avisée, pour le compte rendu ...) –

+0

Je ne pense pas que la redéfinition de ' l'opérateur <'est nécessaire, la valeur par défaut devrait suffire. –

0

Vous ne pouvez pas, pas complètement. Le problème est que vous changez votre interface, ce qui va toujours briser vos clients. Je vous recommande de créer deux nouvelles fonctions de newBegin et newEnd (ou similaire) qui a votre nouveau comportement. Votre ancienne interface vous gardez la même chose mais marquez-la comme dépréciée. La mise en œuvre de cette ancienne interface peut utiliser l'un des travaux décrits par les autres.