此代码尝试在多态指针向量上使用copy_if():
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class AbstractBase
{
public:
virtual bool IsDerived1() const { return false; }
virtual void Print() const = 0;
};
class Derived1 : public AbstractBase
{
public:
virtual bool IsDerived1() const { return true; }
virtual void Print() const { cout << "Derived1" << endl; }
};
class Derived2 : public AbstractBase
{
public:
virtual void Print() const { cout << "Derived2" << endl; }
};
// This function returns the elements of v that are of type Derived1.
vector<Derived1*> SelectDerived1(const vector<AbstractBase*>& v)
{
vector<Derived1*> derived1s;
#define USE_COPY_IF 0
#if USE_COPY_IF
// attempt to use copy_if - does not compile:
// /usr/include/c++/4.7/bits/stl_algo.h:990:6:
// error: invalid conversion from 'AbstractBase*' to 'Derived1*'
// [-fpermissive]
copy_if(v.begin(), v.end(), derived1s.begin(),
[](AbstractBase* elem){ return elem->IsDerived1(); });
#else
for (auto it = v.begin(); it != v.end(); ++it)
if ((*it)->IsDerived1())
derived1s.push_back(static_cast<Derived1*>(*it));
#endif
return derived1s;
}
int main()
{
vector<AbstractBase*> v;
Derived1* d1 = new Derived1;
Derived2* d2 = new Derived2;
v.push_back(d1);
v.push_back(d2);
vector<Derived1*> derived1s = SelectDerived1(v);
for (auto it = derived1s.begin(); it != derived1s.end(); ++it)
(*it)->Print();
delete d1;
delete d2;
return 0;
}
代码编译并正常工作,USE_COPY_IF设置为0:
$ g++ -std=c++11 test_copy_if.cc
$ ./a.out
Derived1
但是我无法通过copy_if()来使其工作 - 请参阅评论中的错误消息。
没有办法吗?
答案 0 :(得分:4)
您可能想要定义一个函数transform_if
(标准中缺少这个函数):
template <class InIt, class OutIt, class Pred, class Trafo>
OutIt transform_if (
InIt begin_in, InIt end_in,
OutIt begin_out,
Pred predicate,
Trafo trafo
) {
OutIt itout = begin_out;
for (InIt itin = begin_in; itin != end_in; ++itin) {
if (predicate (*itin)) {
(*itout) = trafo (*itin);
++itout;
}
}
}
然后你可以写:
transform_if(v.begin(), v.end(), derived1s.begin(),
[](AbstractBase* elem){ return elem->IsDerived1(); },
[](AbstractBase* elem){ return static_cast<Derived1*> (elem); }
);
或者在转换后定义transform_and_keep_if
进行检查:
template <class InIt, class OutIt, class Trafo, class Pred>
OutIt transform_and_keep_if (
InIt begin_in, InIt end_in,
OutIt begin_out,
Trafo trafo,
Pred predicate
) {
OutIt itout = begin_out;
for (InIt itin = begin_in; itin != end_in; ++itin) {
auto transformed = trafo (*itin);
if (predicate (transformed)) {
(*itout) = transformed; // or std::move (transformed)
++itout;
}
}
}
然后写:
transform_and_keep_if(v.begin(), v.end(), derived1s.begin(),
[](AbstractBase* elem){ return dynamic_cast<Derived1*> (elem); },
[](Derived1* elem){ return elem != NULL; },
);
答案 1 :(得分:0)
您可以使用v
在一次传递中对std::partition
进行分区,将所有Derived1
指针放在向量的开头,然后调用std::transform
来实际进行复制。代码如下所示:
auto deriveds_end = std::partition(v.begin(), v.end(),
[](AbstractBase* e){
return e->IsDerived1();
});
std::transform(v.begin(), deriveds_end,
std::back_inserter(derived1s),
[](AbstractBase* e){
return static_cast<Derived1*>(e);
});
这里唯一的问题是std :: partition修改了它的分区序列,这意味着你不能通过const引用传递v
,你可以通过复制或非const引用来传递它(如果当你调用函数时,你不介意v
重新排列。
如果是我,我会坚持循环:
for(auto i : v)
if(i->IsDerived1())
derived1s.push_back(static_cast<Derived1*>(i);
它没有比那更简洁。