如果我有一个指向父类的指针向量,并且该向量是通过实例化从父类派生的对象来初始化的,那么我似乎不能使用基于范围的for循环来获得作为派生对象的元素。下面是一个简单的例子:
#include <vector>
class Parent {};
class Derived : public Parent {};
int main()
{
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Derived* d : objects) { // ERROR
// Use d
}
return 0;
}
是否有一种干净的方法来做我想要的(即,循环遍历派生对象)?我知道可以这样做:
for (Parent* p : objects) {
Derived* d = static_cast<Derived*>(p);
// Use d
}
但是这是c++ 11中最干净的方法吗?还有其他的选择吗?
cppreference.com页面声明基于范围的for循环表达式产生类似于以下代码(__range
, __begin
和__end
仅供说明):
for (range_declaration: range_expression) loop_statement
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
根据这一描述,我推断__begin
和__end
必须是同一类型,事实上,这不是您的情况。指向Parent
的指针不是指向Derived
的指针,它们必须显式强制转换,并且基于范围的for循环根本不执行任何强制转换。
为了实现您正在寻找的东西,我将创建一个助手类,为我执行1转换,例如:
struct Helper
{
Helper(Parent *p) : obj(p) {}
Parent *const obj;
operator Derived *() { return static_cast<Derived *>(obj); }
};
这个帮助器允许我们做以下事情:
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Helper h : objects) {
Derived *d = h;
d->use();
}
我们可以模板化helper类,以便在其他父派生上下文中使用它:
template <typename T> struct THelper
{
THelper(Parent *p) : obj(p) {}
Parent *const obj;
operator T *() { return static_cast<T *>(obj); }
};
...
...
for (THelper<Derived> h : objects) {
Derived *d = h;
d->use();
}
for (THelper<Derived2> h : objects) {
Derived *d = h;
d->use();
}
但是如果你的objects
向量包含混合类型,可能是危险的。
作为改进,您可以将static_cast
替换为dynamic_cast
并检查nullptr
,但您必须意识到它的开销。
这不是一个解决方案,我会用在我的项目,但我希望它看起来干净整洁的你;)
编辑
然而,转换(无论是显式地使用
static_cast
还是通过Helper
类)仍然需要作为for循环内的附加行。
如前所述,强制类型转换(静态或动态)是强制性的,但如果您担心由于添加额外的行而截断循环的整洁性,则重载operator ->
应该可以解决这个问题:
struct Helper
{
Helper(Parent *p) : obj(p) {}
Parent *const obj;
operator Derived *() { return static_cast<Derived *>(obj); }
Derived *operator ->() { return static_cast<Derived *>(obj); }
};
for (Helper h : objects) {
h->use();
}
注意,如果你使用的是operator ->
,你不能检查nullptr
,如果你的objects
向量有混合派生类型,这是必要的。
我已经更新了现场演示以包含新的代码。
1虽然,正如我们在评论中看到的,有更好的方法来实现你的目标。
您应该能够使用dynamic_cast
来实现这一点:
class Parent {
public:
virtual ~Parent() { } // make class polymorphic
};
class Derived : public Parent {};
int main()
{
std::vector<Parent*> objects { new Derived(), new Derived() };
for (Parent* d : objects) { // ERROR
auto temp = dynamic_cast<Derived*>(d);
if (d) // cast succeeded
{
// we found the right class
}
}
范围迭代器在语法上更简洁,不能在for
循环内强制转换。
for (Parent* p : objects) {
Derived* d = static_cast<Derived*>(p);
// Use d
}
如果你是向下铸造,它应该在体内完成。一个设计良好的程序并不需要向下铸造。小心!
p。S:下铸时,dynamic_cast
优于static_cast
。但是当您使用dynamic_cast
时,RTTI会有一点成本。检查dynamic_cast?