Re: Custom iterator - copy constructor problem
Terry West schrieb:
// the iterator ...
template <class Cont, class Iter>
class MyIterator : public iterator_traits<Iter> {
// Add this:
public:
// c'tors
MyIterator(Cont & c, Iter p);
MyIterator(const MyIterator & it);
};
// ... and a container using it:
template <class T>
class MyContainer {
// Add this:
public:
typedef std::vector<T> StorageType;
typedef MyIterator<MyContainer,
StorageType::iterator> iterator;
typedef MyIterator<const MyContainer,
StorageType::const_iterator> const_iterator;
iterator begin() {
return iterator(*this,storage.begin());
}
const_iterator begin() const {
return const_iterator(*this,storage.begin());
}
StorageType storage;
};
In use, I have for example:
typedef MyContainer<int> IntContainer;
typedef IntContainer::iterator IntContainerIt;
typedef IntContainer::const_iterator IntContainerConstIt;
void f(const IntContainer & ic_const) {
IntContainer ic_no_const;
// Here, I can now do, as expected:
// non-const iterator over non-const container
IntContainerIt icn_nit(ic_no_const, ic_no_const.begin());
// const iterator over const container
IntContainerConstIt icc_cit(ic_const, ic_const.begin());
// but I can't, as I would like to, do:
// const iterator over non-const container
IntContainerConstIt icn_cit(ic_no_const, ic_no_const.begin());
}
For this example, my compiler would (correctly, I think) say at
the last line above :
"Can't find a match for
MyIterator<const IntContainer, const int *>
::MyIterator(MyIterator<IntContainer, int *>)"
I would like MyIterator to behave as a 'normal' iterator does
- ie to allow a const MyIterator to be constructed from a non-const
one but not vice versa.
However, as I said at the top (and probably due to my own stupidity!),
I can't seem to find the syntax to achieve this.
Any suggestions?
Note that there are several source code errors, which prevent the
above code to compile:
1) Missing public access to necessary members (I added them
above)
2) All three declared iterators icn_nit, icc_cit, and icn_cit should
not compile but for different reasons. The first reason applies for
all three, namely that you try to assign MyIterator<> instances to
c'tors which expect actually the underlying base iterator of
MyIterator<>. This is because your begin() functions return MyIterator
and *not* StorageType::[const_]iterator. To fix this problem, I
changed your test function f as follows:
void f(const IntContainer & ic_const) {
IntContainer ic_no_const;
// Here, I can now do, as expected:
// non-const iterator over non-const container
IntContainerIt icn_nit(ic_no_const.begin());
// const iterator over const container
IntContainerConstIt icc_cit(ic_const.begin());
// but I can't, as I would like to, do:
// const iterator over non-const container
IntContainerConstIt icn_cit(ic_no_const.begin());
}
Now we have one remaining error left (which is the one I
assume you worry about). To fix this issue we reflect a little
bit upon what you are doing there: You use a constructor
from MyIterator<const MyContainer, StorageType::const_iterator>
which is calleable with one argument and provide a
MyIterator<MyContainer, StorageType::iterator> as argument.
The only available c'tor fulfilling the argument-number
constraints is the copy c'tor you have shown above. But wait:
The argument given is not the target type or a convertible one!
A very simple solution is to add a converting template c'tor
to your iterator class. The proposed solution below uses
your probably existing private members to demonstrate one
further issue, namely the need to declare all
template <typename C, typename OI> class MyIterator
as friends against each other (another solution would be to
provide public available accessors for the private members and
use them in the impl. of the converting c'tor):
template <class Cont, class Iter>
class MyIterator : public std::iterator_traits<Iter> {
private:
Cont* pc;
Iter p;
template <typename C, typename OI>
friend class MyIterator;
public:
// c'tors
MyIterator(Cont& c, Iter p);
// You don't need this - the comp. generated copy c'tor is fine!
//MyIterator(const MyIterator & it);
// This one is necessary to allow conversions:
template <typename OtherIter>
MyIterator(const OtherIter & it) : pc(it.pc), p(it.p) {}
};
A more advanced solution would use SFINAE to
ensure that the converting c'tor is only taken into
account, if Iter is_convertible from OtherIter.
Note also that you can use a similar approach to
reach the effect, that the two-argument c'tor works
with convertible *base* iterators:
template <typename OtherIter>
MyIterator(Cont& c, const OtherIter& p) : pc(&c), p(p) {}
Again, the same recommendations concerning
SFINAE protection apply here as well.
Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]