2

このコードは、ポリモーフィック ポインターのベクターで 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() で動作させることができませんでした - コメントのエラーメッセージを見てください。

方法はありませんか?

4

2 に答える 2

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; },
);
于 2013-08-10T12:52:23.113 に答える
0

v1 つのパスで分割し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 がパーティショニングのシーケンスを変更することです。つまりv、const 参照で渡すことはできません。代わりに、コピーまたは非 const 参照で渡すことができますv(関数を呼び出します)。

しかし、それが私だったら、ループに固執します:

    for(auto i : v)
        if(i->IsDerived1())
            derived1s.push_back(static_cast<Derived1*>(i);

それ以上に簡潔になることはありません。

于 2013-08-10T14:59:04.603 に答える