考虑使用数组的以下算法:
class MyType;
{
// some stuff
}
class MySubType:MyType
{
// some stuff
}
void foo(MyType** arr, int len)
{
for (int i = 0;i<len;i++)
// do something on arr[i]->
}
void bar()
{
MySubType* arr[10];
// initialize all MySubType*'s in arr
foo(&arr, 10);
}
这里没什么特别的。我的问题是 - 如何使用模板执行此操作?
void foo(std::vector<MyType>& s)
{
std::vector<MyType>::iterator i;
for (i = s.begin(); i != s.end(); i++)
// do stuff on *i
}
所以,在吧,我不能这样做:
void bar()
{
std::vector<MySubType> s;
foo(s); // compiler error
}
错误:从类型std::vector<MyType, std::allocator<MyType> >&
std::vector<MySubType, std::allocator<MySubType> >
类型的引用无效
有没有办法做这样的事情?
基本上,如果有办法做到这一点:
std::vector<MySubType> s;
std::vector<MyType>& t = s;
我很高兴......
答案 0 :(得分:8)
这可能会解决您的问题
template <typename T>
void foo(std::vector<T>& s)
{
typename std::vector<T>::iterator i;
for (i = s.begin(); i != s.end(); i++)
// do stuff on *i
}
答案 1 :(得分:6)
要扩展kuoson's answer,惯用的C ++风格是将迭代器传递给函数而不是容器。
template<typename Iterator>
void foo(const Iterator & begin, const Iterator & end)
{
Iterator i;
for (i = begin; i != end; ++i)
// do stuff on *i
}
答案 2 :(得分:3)
这就是问题所在 - 如果s和t指向同一个对象,那么阻止你将MyOtherSubType
(与MySubType无关)放入t中的是什么?这将使s包含不是MySubType的对象。我不知道任何类型安全的编程语言可以让你这样做。如果允许的话,想象一下我们遇到的问题:
//MySubType inherits from MyType
//MyOtherSubType also inherits from MyType
std::vector<MySubType> s;
std::vector<MyType>& t = s;
MyOtherSubType o;
t.push_back(o);
由于t和s在不同名称下是完全相同的对象,因此s [0]处的元素不是MySubType。巨大的问题 - 我们永远无法确定我们的向量是否包含它们应该具有的类型!因此,编译不允许它。
答案 3 :(得分:2)
既然你说“这里没什么特别的东西”,我不确定你是否意识到这一点:
您的原始代码已损坏;或者,如果你很幸运,它现在可能不会被打破,但是它很脆弱,只要有人做了一些事情而不是看MySubType
课程就会打破。
问题在于您将MyType*
传递给foo()
,但它确实指向MySubType
的数组。如果MySubType
对象恰好大于MyType
对象(如果你已经向派生类添加了任何东西,那很可能),那么在foo()
函数中完成指针算术将是不正确的。
这是派生类对象数组的严重和经典陷阱之一。
答案 4 :(得分:1)
如果你想在一个向量中存储多个不同MyType
派生类型的对象(我怀疑你这样做,虽然这个特定的例子没有必要),你需要使用{{ 1}}而不是std::vector<MyType*>
。这个建议类似于Michael Burr提出的原始指针代码。
这确实有一个令人遗憾的副作用,你不能隐含地将std::vector<MyType>
转换为std::vector<MySubType*>
来调用std::vector<MyType*>
。但转换代码并不太繁琐:
foo()
或者,让void foo(std::vector<MyType*>& s)
{
...
}
void bar()
{
std::vector<MySubType*> s;
// Populate s
...
std::vector<MyType*> u(s.begin(), s.end()); // Convert
foo(u);
}
从头开始使用bar()
。
答案 5 :(得分:0)
C ++不支持模板类型的协方差,不仅仅是C#目前所做的,出于同样的原因。如果你想这样做,最好让你的矢量在一个通用的接口类型(指向那种类型的指针)上进行模板处理,并使函数foo接受这种类型的矢量。
答案 6 :(得分:0)
不幸的是,没有。不同的模板特化被认为是不同的类;您无法在std::vector<MyType>
和std::vector<MySubType>
之间进行转换。
你可以将foo()
的“do stuff”公共位拆分成一个单独的函数,并在每个向量上有一个单独的循环(或者可能使用std::foreach
)。
答案 7 :(得分:0)
使用boost(用于智能指针):
foo( std::vector<boost::shared_ptr<MyType> >& v )
{
std::for_each( v.begin(),
v.end(),
do_something );
}
bar()
{
std::vector<boost::shared_ptr<MyType> > s;
// s.push_back( boost::shared_ptr<MyType> ( new MySubType() ) );
foo( s );
}
答案 8 :(得分:0)
如果我知道那些foobars背后的实际代码是什么,那么也许我会更清楚,但是在STL中你的问题是不是已经解决了?
for_each(s.begin(), s.end(), DoStuffOnI());
只需将您的“在* i上执行代码”代码放在函数或函数中:
struct DoStuffOnI : public std::unary_function<MyType&,void> {
void operator()(MyType& obj) {
// do stuff on *i
}
};
如果你对发送两个参数而不是一个参数感到困扰,那么好吧,也许你可以这样做:
template<typename In>
struct input_sequence_range : public std::pair<In,In> {
input_sequence_range(In first, In last) : std::pair<In,In>(first, last)
{
}
};
template<typename C>
input_sequence_range<typename C::iterator> iseq(C& c)
{
return input_sequence_range<typename C::iterator>(c.begin(), c.end());
}
template<typename In, typename Pred>
void for_each(input_sequence_range<In> r, Pred p) {
std::for_each(r.first, r.second, p);
}
然后像这样调用for_each:
for_each(iseq(s), DoStuffOnI());