避免强制转换容器的元素指针类型

avoid to cast element pointer type of a container

本文关键字:元素 指针 类型 转换      更新时间:2023-10-16

我有一个函数,其中我需要指向BaseDerived类型的指针向量。我有一个函数,它应该能够处理这两种容器类型,因为它只调用所有在Base中定义的虚拟函数。

如果给定元素指针,直接是没有问题的,比如:

void Func(Base*);
Derived* d;
Func(d);  // works perfect

但如果有一个集装箱类型,它就会变得丑陋而危险。如果且仅当容器类型对两种指针类型都完全等效时,强制转换才会起作用。这通常是真的,因为所有已知的标准容器实现都实现了从void*派生的指针类型,以最大限度地减少占用空间。

#include <iostream>
#include <vector>
class Base
{   
public: virtual void Func() {std::cout << "Base" << std::endl; }
};  
class Derived: public Base
{   
public:
void Func() override { std::cout << "Derived" << std::endl; }
void MoreFunc(){ std::cout << "SomeMoreFunc" << std::endl; } 
};  
// This function should "eat" also vector<Derived*>
// this can be simply done by using templates, 
// but results in doubling the code in memory while
// two instances will be created with exact the same
// content and they will not be optimized away ( gcc ).
void DoSomething( std::vector<Base*>& vec )
{   
for ( auto& el: vec ) { el->Func();}   
}   
int main()
{   
std::vector< Base*> vecBase;
vecBase.push_back( new Base );
vecBase.push_back( new Base );
std::vector< Derived*> vecDerived;
vecDerived.push_back( new Derived );
vecDerived.push_back( new Derived );
DoSomething( vecBase );
DoSomething( (std::vector<Base*>&)vecDerived ); // that cast I want to avoid!
for ( auto& el: vecDerived ) { el->MoreFunc(); }   
}   

如何避免向量元素类型的错误转换?做一个完整的容器副本是一种选择,但也浪费代码和运行时间。

对于这些问题,有"典型"的解决方案吗?

是的,DoSomething内部更为复杂,正如这个最小的例子所给出的那样。因此,简单地将for()删除到foreach/lambda是没有机会的,而且从外部调用DoSomething内部的函数也不适合我在这里遇到的现实问题。而且必须记住,for_each本身将在这里生成更多的模板实例,这与我想要避免的恰恰相反。

您可以使用<algorithm>中的std::for_each和lambda来实现这一点:

auto func = [](Base *ptr) { ptr->Func(); };
std::for_each(vecBase.begin(), vecBase.end(), func);
std::for_each(vecDerived.begin(), vecDerived.end(), func);

或者如下定义您的功能:

void DoSomething(Base* ptr) {   
ptr->Func();
}

并与std::for_each:一起使用

std::for_each(vecBase.begin(), vecBase.end(), DoSomething);
std::for_each(vecDerived.begin(), vecDerived.end(), DoSomething);

在这两种情况下都没有模板
老实说,代码中没有模板。std::for_each是一个函数模板,但DoSomething不会。不确定这是你想要的。


评论后编辑

避免模板的最好方法是从DoSomething中获得循环
在这种情况下,任何接受Base *的函数都可以
另一方面,如果你想在函数中迭代,你可能无法通过传递一个容器(模板)、传递两个迭代器(模板)或基于假设进行花式转换(有风险)。

请注意,向量的成员函数data返回指向底层数组的指针。如果与size结合使用,您将拥有对元素进行迭代所需的所有内容
无论如何,在您的情况下,Base **Derived **是不同的野兽,您不能简单地将Derived **传递给接受Base **的函数。

任何使用标准库中函数的尝试(例如std::for_each)都可能以某种方式使用模板函数而告终。

这是因为std::vector<Base *>std::vector<Derived *>不同的类型。