Reputation: 66244
I have a class Foo
that contains a map
and provides begin()
and end()
functions to iterate over it:
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; }
// ...
};
Now I would like to change it internally from std::map<int, double>
to just a std::set<int>
, but I don't want to break any client code.
So the double d
in the insert
function would now just be ignored. And the following code should still be valid, where it->second
will now just always be 0.0
:
Foo foo;
for(Foo::const_iterator it = foo.begin(); it != foo.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
How can I make these changes in the Foo
class?
In other words, how can I provide a Foo::const_iterator
that adapts the new internal std::set<int>::const_iterator
to behave like the old std::map<int,double>::const_iterator
?
UPDATE: The reason I want to get rid of the map
is memory efficiency. I have millions of Foo
instances and cannot afford to store the double
values in them.
Upvotes: 5
Views: 252
Reputation: 10562
You can't, not completely. The problem is you are changing your interface, which will always break your clients. I would recommend you create two new functions of newBegin and newEnd (or similar) which has your new behaviour. Your old interface you keep this the same but mark it as depreciated. The implementation of this old interface can use one of the work around described by the others.
Upvotes: 0
Reputation: 20656
How about something like this?
#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;
}
Upvotes: 1
Reputation: 88801
Would using
std::set<std::pair<int, double> >
not be sufficient for this comparability?
Failing that you can always write your own iterator which wraps the std::list iterator and provides first
and second
members. Basically your operator++ would call operator++ on the real iterator etc. and the de-referencing operator could return either a temporary std::pair (by value) or a reference to a std::pair that lives within the iterator itself (if your legacy code can deal with that).
Update, slightly contrived example, might work depending on your scenario:
#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;
}
}
Upvotes: 2
Reputation: 1474
Perhaps you can define a fake_pair
class that implements first
and second
and put a set<fake_pair>
inside Foo
.
Upvotes: 0
Reputation: 58715
Perhaps something along the lines of
operator int()(const std::pair<int, double>& p) const {
return p.first;
}
maybe within some wrapper?
Upvotes: 0