C++ Const方法和自定义迭代器
我有一个类包含一个C++ Const方法和自定义迭代器,c++,oop,templates,iterator,C++,Oop,Templates,Iterator,我有一个类包含一个std::vector,另一个类是该类的自定义迭代器。它对于非常量方法非常有效,但当我尝试在const方法中创建迭代器时,编译器失败,因为它无法将const\u迭代器转换为迭代器。例如,下面的代码失败,但如果对ConstFunc的调用被注释掉,则编译并运行良好 #include <vector> template <class T> class FooIter { public: typename std::vector<T>::i
std::vector
,另一个类是该类的自定义迭代器。它对于非常量方法非常有效,但当我尝试在const
方法中创建迭代器时,编译器失败,因为它无法将const\u迭代器
转换为迭代器
。例如,下面的代码失败,但如果对ConstFunc
的调用被注释掉,则编译并运行良好
#include <vector>
template <class T>
class FooIter {
public:
typename std::vector<T>::iterator iterator;
FooIter(std::vector<T> &vec) {iterator = vec.begin();}
};
template <class T>
class Foo {
public:
std::vector<T> vec;
Foo() {}
void NonConstFunc() {
FooIter<T> iter(vec);
}
void ConstFunc() const {
FooIter<T> iter(vec);
}
};
int main()
{
Foo<int> foo;
foo.NonConstFunc();
foo.ConstFunc();
return 0;
}
Matrix2d
是“Foo”,而Matrix2dIterator
是“FooIter”find
需要迭代保存矩阵数据的std::vector
,但它不会更改该数据。原因是,您可以通过std::vector::iterator
修改vec
,从而修改类。因此,不能将它们放在const
成员函数中。更技术性的原因是,在const
成员函数中,所有成员都是const
,并且std::vector
具有这些函数
std::vector<T>::iterator begin();
std::vector<T>::const_iterator begin() const;
std::vector<T>::const_iterator cbegin() const;
或者,不在向量的值类型上设置模板,而是在向量本身上设置模板,这会自动检测const
-ness:
#包括
样板
福伊特级{
公众:
decltype(std::declval().begin())迭代器;
FooIter(T&vec){iterator=vec.begin();}
};
样板
福班{
公众:
std::vec;
Foo(){}
void nonstfunc(){
国际热核聚变实验堆(vec);
}
void ConstFunc()常量{
国际热核聚变实验堆(vec);
}
};
int main()
{
富富,;
foo.noncostfunc();
foo.ConstFunc();
返回0;
}
请注意,这并不意味着您只剩下一个类。两种方法中的iter
s将是不同类型的(FooIter
vsFooIter
)。优点是编译器将为您编写这两个类,从而减少代码重复,因为这两个类几乎相同
这是不利的。要知道现在使用的是哪个变量并不容易,在非const
上下文中使用const
迭代器(如果不打算修改任何内容,您应该始终这样做)有点棘手,因为FooIter(vec)
在vec
不是const时总是返回非const
变量
编辑:添加了一些模板魔术,只允许std::vector
,即std::set s;FooIter f{s}代码>将不再编译。您应该选择n314159的解决方案之一。我只是想说明使FooIter
同时适用于非常量和常量向量的问题
您可以将重载构造函数添加到FooIter
,该构造函数采用常量引用:
FooIter(const std::vector<T> &vec) {iterator = vec.begin();}
但是其他成员函数需要知道使用哪一个。您可以为此添加一个标志变量,但这将是相当低效的;它需要更多的内存(除了迭代器本身的重复),并且需要对每个成员函数调用进行检查。因此,最好的解决方案是有两个独立的迭代器类,一个用于非常量,另一个用于常量。也许
#include <vector>
using namespace std;
template <class T>
class FooIter {
public:
const typename vector<T>::iterator itr; // const added
FooIter(vector<T> &vec) : itr{vec.begin()} {}
};
template <class T>
class Foo {
public:
vector<T> vec;
Foo() {}
void NonConstFunc() {
FooIter<T> iter(vec);
}
void ConstFunc() { // const removed, if
FooIter<T> iter(vec); // needed prefix it on each needed line
}
};
int main() {
Foo<int> foo;
foo.NonConstFunc();
foo.ConstFunc();
return 0;
}
#包括
使用名称空间std;
样板
福伊特级{
公众:
常量typename向量::迭代器itr;//添加了常量
FooIter(vector&vec):itr{vec.begin()}{}
};
样板
福班{
公众:
向量向量机;
Foo(){}
void nonstfunc(){
国际热核聚变实验堆(vec);
}
void ConstFunc(){//const已删除,如果
FooIter iter(vec);//需要在每一行加上前缀
}
};
int main(){
富富,;
foo.noncostfunc();
foo.ConstFunc();
返回0;
}
您可能需要一个FooIter
和一个ConstFooIter
,因为iterator
和const\u iterator
是不同的类型。你可以用union
/variant
做一些奇怪的事情,但这会让你的代码难以阅读。以我的经验,试图使iterator
和const\u iterator
类型相同是行不通的。您计划如何在const
方法中使用FooIter
呢?您可以尝试对FooIter
类使用一些额外的模板参数化,但通常情况下,为iterator和const使用两种不同的类型比较容易常量迭代器。@rustyx我在问题中添加了一个我打算如何使用常量迭代器的示例。伙计,我不知道我对模板的了解有多少。我甚至不知道std::enable_if_t之类的东西存在。谢谢你启发我,并把这些放在一起代码>行,我应该为FooIter使用什么模板专门化?我知道它应该是类似于国际热核实验堆(vec)的东西
,但我不知道用什么来替换问号。只需使用FooIter
或FooIter
。第二个参数有缺省值,不必(实际上也不应该)提供。@ JimLoad模板元编程是C++中一个有趣的兔子洞。
typename std::vector<T>::iterator iterator;
typename std::vector<T>::const_iterator const_iterator;
FooIter(std::vector<T> &vec) {iterator = vec.begin();}
FooIter(const std::vector<T> &vec) {const_iterator = vec.begin();}
#include <vector>
using namespace std;
template <class T>
class FooIter {
public:
const typename vector<T>::iterator itr; // const added
FooIter(vector<T> &vec) : itr{vec.begin()} {}
};
template <class T>
class Foo {
public:
vector<T> vec;
Foo() {}
void NonConstFunc() {
FooIter<T> iter(vec);
}
void ConstFunc() { // const removed, if
FooIter<T> iter(vec); // needed prefix it on each needed line
}
};
int main() {
Foo<int> foo;
foo.NonConstFunc();
foo.ConstFunc();
return 0;
}