在 C++ 中实现 const_iterator 时遇到问题
Trouble implementing const_iterator in c++
我正在努力尝试对我的容器 class 实施 const_iterator,我用一个基本数组做了一个简短的可重现示例来说明问题(我知道 std::iterator
已弃用,但这是针对 C++98 标准中的项目):
#include <iostream>
#include <cstdlib>
#include <iterator>
template <
typename T
> struct remove_const { typedef T type; };
template <
typename T
> struct remove_const < const T > { typedef T type; };
template <
typename T
> struct add_const { typedef const T type; };
template <
class T,
std::size_t size
> class Array {
public:
template <
class U
> class base_iterator : public std::iterator <
std::random_access_iterator_tag,
U
> {
public:
typedef std::iterator <
std::random_access_iterator_tag,
U
> iterator_type;
base_iterator() : ptr(NULL) { }
base_iterator(U *p) : ptr(p) { }
base_iterator(const base_iterator &other) : ptr(other.ptr) { }
base_iterator(const base_iterator< remove_const< U > > &other) : ptr(other.ptr) { }
base_iterator(const base_iterator< add_const< U > > &other) : ptr(other.ptr) { }
base_iterator &operator =(const base_iterator &other) {
ptr = other.ptr;
return *this;
}
base_iterator &operator =(const base_iterator< remove_const< U > > &other) {
ptr = other.ptr;
return *this;
}
base_iterator &operator =(const base_iterator< add_const< U > > &other) {
ptr = other.ptr;
return *this;
}
U &operator *(void) { return *ptr; }
U &operator *(void) const { return *ptr; }
U *operator ->(void) { return ptr; }
U *operator ->(void) const { return *ptr; }
base_iterator &operator ++(void) {
++ptr;
return *this;
}
base_iterator operator ++(int) {
return base_iterator(ptr++);
}
base_iterator &operator --(void) {
--ptr;
return *this;
}
base_iterator operator --(int) {
return base_iterator(ptr++);
}
base_iterator operator +(const typename iterator_type::difference_type &amount) const {
return base_iterator(ptr + amount);
}
base_iterator operator -(const typename iterator_type::difference_type &amount) const {
return base_iterator(ptr - amount);
}
base_iterator &operator +=(const typename iterator_type::difference_type &amount) {
ptr += amount;
return *this;
}
base_iterator &operator -=(const typename iterator_type::difference_type &amount) {
ptr -= amount;
return *this;
}
bool operator ==(const base_iterator &other) const {
return ptr == other.ptr;
}
bool operator !=(const base_iterator &other) const {
return ptr != other.ptr;
}
bool operator <(const base_iterator &other) const {
return ptr < other.ptr;
}
bool operator <=(const base_iterator &other) const {
return ptr <= other.ptr;
}
bool operator >(const base_iterator &other) const {
return ptr > other.ptr;
}
bool operator >=(const base_iterator &other) const {
return ptr >= other.ptr;
}
private:
U *ptr;
};
typedef base_iterator< T > iterator;
typedef base_iterator< const T > const_iterator;
typedef std::reverse_iterator< iterator > reverse_iterator;
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
iterator begin(void) { return iterator(data); }
const_iterator begin(void) const { return const_iterator(data); }
iterator end(void) { return iterator(data + size); }
const_iterator end(void) const { return const_iterator(data + size); }
reverse_iterator rbegin(void) { return reverse_iterator(data + size - 1); }
const_reverse_iterator rbegin(void) const { return const_reverse_iterator(data + size - 1); }
reverse_iterator rend(void) { return reverse_iterator(data - 1); }
const_reverse_iterator rend(void) const { return const_reverse_iterator(data - 1); }
private:
T data[size];
};
int main(void) {
Array< int, 20 > a;
for (Array< int, 20 >::iterator it = a.begin(); it != a.end(); ++it)
*it = 42;
for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
std::cout << *it << std::endl;
}
编译器输出
problem.cpp:106:40: error: no viable conversion from 'base_iterator<int>' to 'base_iterator<const int>'
for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
^ ~~~~~~~~~
problem.cpp:24:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const int *' for 1st argument
base_iterator(U *p) : ptr(p) { }
^
problem.cpp:25:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const Array<int, 20>::base_iterator<const int> &' for 1st argument
base_iterator(const base_iterator &other) : ptr(other.ptr) { }
^
1 error generated.
所以问题似乎是 base_iterator< T >
不能转换为 base_iterator< const T >
,但首先我想知道为什么我们需要转换?由于 begin
和 end
以及 operator */->
有 const 重载,它不应该调用 const 版本吗?
其次,如果我们需要转换,我不知道该怎么做
编辑 1:我实现了 remove_const
和 add_const
来进行转换,但它仍然无法编译
是否调用成员函数的const
限定版本,仅取决于调用成员函数的表达式类型是否为const
限定。
在 a.begin()
中,在您的示例中 a
被声明为 Array< int, 20 >
而不是 const
。因此 begin()
的非 const
限定版本将被调用。
如果您想让您的容器 class 符合 C++ 标准库中的 Container 要求,那么您必须使您的 iterator
可转换为const_iterator
。这是规定的要求之一,将使您的测试代码按预期工作。
有关 Container 要求的完整列表,请参阅 cppreference。
您的编译器告诉您它无法将您的 iterator
转换为您的 const_iterator
。
a.begin();
由于 a
是一个可变对象,begin()
重载 return 一个 iterator
被调用,但尝试将结果存储到const_iterator
,并且您设计迭代器的方式 类 不允许进行该转换。
这就是为什么您不能将 std::vector<int>
像那样分配给 std::vector<const int>
的原因。这是相同的根本原因。 std::vector<int>
和 std::vector<const int>
是两个完全不同的 类,它们在任何形式或方式上都没有关系。你的两个迭代器 类 在同一条船上。
您需要做的是重新设计您的迭代器 类 以便这种转换变得微不足道。
处理这个问题的经典方法是让 iterator
继承自 const iterator
,例如:
class const_iterator {
protected:
T *p; // The underlying pointer, or whatever is being used to reference the iterator value and/or its container.
public:
// Implement only the `const` methods here
};
class iterator : public const_iterator {
public:
// Implement only the non-const methods here
};
现在,神奇的转换会在需要时自动发生。 iterator
和 const_iterator
都可以完全访问底层容器及其值。 const_iterator
只需要足够小心以保持 const
-正确性,即它的 operator*
重载 return a const T &
,而 iterator::operator*()
return是一个T &
,依此类推。
我正在努力尝试对我的容器 class 实施 const_iterator,我用一个基本数组做了一个简短的可重现示例来说明问题(我知道 std::iterator
已弃用,但这是针对 C++98 标准中的项目):
#include <iostream>
#include <cstdlib>
#include <iterator>
template <
typename T
> struct remove_const { typedef T type; };
template <
typename T
> struct remove_const < const T > { typedef T type; };
template <
typename T
> struct add_const { typedef const T type; };
template <
class T,
std::size_t size
> class Array {
public:
template <
class U
> class base_iterator : public std::iterator <
std::random_access_iterator_tag,
U
> {
public:
typedef std::iterator <
std::random_access_iterator_tag,
U
> iterator_type;
base_iterator() : ptr(NULL) { }
base_iterator(U *p) : ptr(p) { }
base_iterator(const base_iterator &other) : ptr(other.ptr) { }
base_iterator(const base_iterator< remove_const< U > > &other) : ptr(other.ptr) { }
base_iterator(const base_iterator< add_const< U > > &other) : ptr(other.ptr) { }
base_iterator &operator =(const base_iterator &other) {
ptr = other.ptr;
return *this;
}
base_iterator &operator =(const base_iterator< remove_const< U > > &other) {
ptr = other.ptr;
return *this;
}
base_iterator &operator =(const base_iterator< add_const< U > > &other) {
ptr = other.ptr;
return *this;
}
U &operator *(void) { return *ptr; }
U &operator *(void) const { return *ptr; }
U *operator ->(void) { return ptr; }
U *operator ->(void) const { return *ptr; }
base_iterator &operator ++(void) {
++ptr;
return *this;
}
base_iterator operator ++(int) {
return base_iterator(ptr++);
}
base_iterator &operator --(void) {
--ptr;
return *this;
}
base_iterator operator --(int) {
return base_iterator(ptr++);
}
base_iterator operator +(const typename iterator_type::difference_type &amount) const {
return base_iterator(ptr + amount);
}
base_iterator operator -(const typename iterator_type::difference_type &amount) const {
return base_iterator(ptr - amount);
}
base_iterator &operator +=(const typename iterator_type::difference_type &amount) {
ptr += amount;
return *this;
}
base_iterator &operator -=(const typename iterator_type::difference_type &amount) {
ptr -= amount;
return *this;
}
bool operator ==(const base_iterator &other) const {
return ptr == other.ptr;
}
bool operator !=(const base_iterator &other) const {
return ptr != other.ptr;
}
bool operator <(const base_iterator &other) const {
return ptr < other.ptr;
}
bool operator <=(const base_iterator &other) const {
return ptr <= other.ptr;
}
bool operator >(const base_iterator &other) const {
return ptr > other.ptr;
}
bool operator >=(const base_iterator &other) const {
return ptr >= other.ptr;
}
private:
U *ptr;
};
typedef base_iterator< T > iterator;
typedef base_iterator< const T > const_iterator;
typedef std::reverse_iterator< iterator > reverse_iterator;
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
iterator begin(void) { return iterator(data); }
const_iterator begin(void) const { return const_iterator(data); }
iterator end(void) { return iterator(data + size); }
const_iterator end(void) const { return const_iterator(data + size); }
reverse_iterator rbegin(void) { return reverse_iterator(data + size - 1); }
const_reverse_iterator rbegin(void) const { return const_reverse_iterator(data + size - 1); }
reverse_iterator rend(void) { return reverse_iterator(data - 1); }
const_reverse_iterator rend(void) const { return const_reverse_iterator(data - 1); }
private:
T data[size];
};
int main(void) {
Array< int, 20 > a;
for (Array< int, 20 >::iterator it = a.begin(); it != a.end(); ++it)
*it = 42;
for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
std::cout << *it << std::endl;
}
编译器输出
problem.cpp:106:40: error: no viable conversion from 'base_iterator<int>' to 'base_iterator<const int>'
for (Array< int, 20 >::const_iterator it = a.begin(); it != a.end(); ++it)
^ ~~~~~~~~~
problem.cpp:24:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const int *' for 1st argument
base_iterator(U *p) : ptr(p) { }
^
problem.cpp:25:5: note: candidate constructor not viable: no known conversion from 'Array<int, 20>::iterator' (aka 'base_iterator<int>') to 'const Array<int, 20>::base_iterator<const int> &' for 1st argument
base_iterator(const base_iterator &other) : ptr(other.ptr) { }
^
1 error generated.
所以问题似乎是 base_iterator< T >
不能转换为 base_iterator< const T >
,但首先我想知道为什么我们需要转换?由于 begin
和 end
以及 operator */->
有 const 重载,它不应该调用 const 版本吗?
其次,如果我们需要转换,我不知道该怎么做
编辑 1:我实现了 remove_const
和 add_const
来进行转换,但它仍然无法编译
是否调用成员函数的const
限定版本,仅取决于调用成员函数的表达式类型是否为const
限定。
在 a.begin()
中,在您的示例中 a
被声明为 Array< int, 20 >
而不是 const
。因此 begin()
的非 const
限定版本将被调用。
如果您想让您的容器 class 符合 C++ 标准库中的 Container 要求,那么您必须使您的 iterator
可转换为const_iterator
。这是规定的要求之一,将使您的测试代码按预期工作。
有关 Container 要求的完整列表,请参阅 cppreference。
您的编译器告诉您它无法将您的 iterator
转换为您的 const_iterator
。
a.begin();
由于 a
是一个可变对象,begin()
重载 return 一个 iterator
被调用,但尝试将结果存储到const_iterator
,并且您设计迭代器的方式 类 不允许进行该转换。
这就是为什么您不能将 std::vector<int>
像那样分配给 std::vector<const int>
的原因。这是相同的根本原因。 std::vector<int>
和 std::vector<const int>
是两个完全不同的 类,它们在任何形式或方式上都没有关系。你的两个迭代器 类 在同一条船上。
您需要做的是重新设计您的迭代器 类 以便这种转换变得微不足道。
处理这个问题的经典方法是让 iterator
继承自 const iterator
,例如:
class const_iterator {
protected:
T *p; // The underlying pointer, or whatever is being used to reference the iterator value and/or its container.
public:
// Implement only the `const` methods here
};
class iterator : public const_iterator {
public:
// Implement only the non-const methods here
};
现在,神奇的转换会在需要时自动发生。 iterator
和 const_iterator
都可以完全访问底层容器及其值。 const_iterator
只需要足够小心以保持 const
-正确性,即它的 operator*
重载 return a const T &
,而 iterator::operator*()
return是一个T &
,依此类推。