2

方法はありますか

container< container<Base> >

container<Derived>一緒に保持したい s の束がある場合 (イテレータに?)

具体例を次に示します。

あなたが持っていると言う

struct Animal { } ;
struct Dog : public Animal { } ;
struct StripedDog : public Dog { } ;
struct Cat : public Animal { } ;
struct SpottedCat : public Cat { } ;

Cats、SpottedCats、Dogs、StripedDogs のコレクションをベクトルまたはリストで自然に保持したい場合、

vector<Dog*> doggies ;
vector<StripedDog*> stripedDoggies ;
vector<Cat*> catties ;
vector<SpottedCat*> spottedCatties ;

ただし、すべての動物を反復処理したいので、すべての犬と猫のコレクションへの参照を 1 つのオブジェクトにまとめたいとします。

vector< vector<Animal *>* > zoo ;

zoo.push_back( &doggies ) ;
zoo.push_back( &stripedDoggies ) ;
zoo.push_back( &catties ) ;
zoo.push_back( &spottedCatties ) ;

だから今、あなたはすることができます

feed( zoo ) ;

もちろん、これはコンパイルされません。ネコとイヌのベクトルはvector<Animal*>ではなく、具体的な型のベクトルです。冗長リストを保持せず、具体的な型情報を失うことなく (つまり、基本型のリストを使用しないAnimal*) vector<Animal*> stripedDoggies、C++ から同等の動作を実現する方法はありますか?

4

4 に答える 4

0

ポリモーフィック イテレータ? 身震い

使用例:

#include <iostream>
struct Animal
{
    virtual void print() = 0;
};

struct Elephant : Animal
{
    virtual void print() override { std::cout << "toot"; }
};
struct Cat : Animal
{
    virtual void print() override { std::cout << "meow"; }
};
struct Fox : Animal
{
    virtual void print() override
    { std::cout << "ring-ding-ding-ding-dingeringeding"; }
};

#include <vector>

template<class T>
using simple_vector = std::vector<T>;

int main()
{
    std::vector<Elephant> elephants(5);
    std::vector<Cat> cats(3);
    std::vector<Fox> foxes(1);

    polymorphic_range_container<simple_vector, Animal> animals;
    animals.push_back( std::make_pair(elephants.begin(), elephants.end()) );
    animals.push_back( std::make_pair(cats.begin(), cats.end()) );
    animals.push_back( std::make_pair(foxes.begin(), foxes.end()) );

    for(auto& animal : animals)
    {
        animal.print(); std::cout << ", ";
    }
    std::cout << std::endl;
}

実装 (基本):

#include <memory>
#include <algorithm>
#include <iterator>
#include <utility>

template<class T>
struct iterator_base
{
    virtual void advance(int i) = 0;
    virtual T& get() const = 0;
    virtual bool equal(iterator_base const&) const = 0;
};

template<class ValueType, class Container, class Id>
struct polymorphic_iterator
{
    polymorphic_iterator& operator++()
    {
        impl->advance(1);
        if(container->is_end(*impl, id))
        {
            impl = container->next(id);
        }

        return *this;
    }

    ValueType& operator*() const {  return impl->get();  }

    friend bool operator==(polymorphic_iterator const& l,
                           polymorphic_iterator const& r)
    {
        if(l.impl == nullptr) return r.impl == nullptr;
        if(r.impl == nullptr) return false;

        return l.impl->equal( *(r.impl) );
    }
    friend bool operator!=(polymorphic_iterator const& l,
                           polymorphic_iterator const& r)
    {
        return not (l == r);
    }

private:
    std::unique_ptr< iterator_base<ValueType> > impl;
    Container* container;
    Id id;

    friend Container;
    polymorphic_iterator(Container* pc, Id pid, decltype(impl) p)
    : impl( std::move(p) ), container(pc), id(std::move(pid))
    {}
};

template<template<class> class Container, class Base>
class polymorphic_range_container
{
private:
    using self = polymorphic_range_container;

    struct IRange
    {
        using piterator = std::unique_ptr < iterator_base<Base> >;
        virtual piterator begin() = 0;
        virtual piterator end() = 0;
    };

    template<class It>
    struct range : IRange
    {
        range(It p_begin, It p_end) : m_begin(p_begin), m_end(p_end) {}

        using typename IRange::piterator;
        piterator begin() override { return piterator{new iterator_impl(m_begin)}; }
        piterator end() override { return piterator{new iterator_impl(m_end)}; }

    private:
        struct iterator_impl : iterator_base<Base>
        {
            iterator_impl(It p) : it(p) {}

            virtual void advance(int i) override { std::advance(it, i); }
            virtual Base& get() const override { return *it; }
            virtual bool equal(iterator_base<Base> const& other) const override
            {
                iterator_impl const* pOther
                    = dynamic_cast<iterator_impl const*>(&other);
                if(nullptr == pOther) return false;
                else return it == pOther->it;
            }
        private:
            It it;
        };

        iterator_impl m_begin;
        iterator_impl m_end;
    };

    using container_type = Container< std::unique_ptr<IRange> >;
    container_type ranges;

public:
    template<class T>
    void push_back(std::pair<T, T> p_range)
    {
        ranges.push_back( std::unique_ptr<IRange>{new range<T>(p_range.first, p_range.second)} );
    }

    using iterator = polymorphic_iterator<Base, self, typename container_type::const_iterator>;
    iterator begin()
    {
        return iterator{this, ranges.cbegin(), ranges.front()->begin()};
    }
    iterator end()
    {
        return iterator{this, ranges.cend(), {nullptr}};
    }

private:
    friend iterator;
    std::unique_ptr< iterator_base<Base> > next(typename container_type::const_iterator& p)
    {
        ++p;

        if(p == ranges.end()) return {nullptr};
        else return (**p).begin();
    }
    bool is_end(iterator_base<Base> const& it, typename container_type::const_iterator const& id)
    {
        if(ranges.end() == id) return false;
        else return (**id).end()->equal(it);
    }
};
于 2013-10-10T01:58:50.240 に答える
0

コピーが非常に安価なポインターを使用しているため、おそらく次のことができます。

vector< Animal * > zoo;

zoo.append( zoo.end(), doggies.begin(), doggies.end() );
// ditto with the others

feed( zoo ); // just receives *one* vector with animals to feed

ベクトルをコピー/マージしたくない場合の別のオプション:

void feed() {}

template< typename V >
void feed( const V& v )
{
    for( A* a : v )
    {
        // ...do something with 'a'
    }
}

template< typename V, typename V2, typename... Vs >
void feed( const V& v, const V2& v2, const Vs&... vs )
{
    feed( v );
    feed( v2, vs... );
}

これで、 を呼び出すことができますfeed( doggies, stripedDoggies, catties, spottedCatties );

于 2013-10-09T23:54:22.530 に答える
0

一般に、このようなことをしている場合は、それらすべてを 1 つのベクトルに格納します。

std::vector<std::shared_ptr<Animal>> animals;

Animalメソッドを定義する場合feed、それを反復することは単にその関数を呼び出すことを意味します。

animals[i]->feed();

型に基づいて特定の関数を呼び出したい場合は、いくつかのキャストを行う必要があります。

std::shared_ptr<Dog> pDog = std::dynamic_pointer_cast<Dog>(animals[i]);
std::shared_ptr<Cat> pCat = std::dynamic_pointer_cast<Cat>(animals[i]);
// other casts
if (pDog)
{
    // do something with a dog
}
else if (pCat)
{
    // do something with a cat
}
// etc

animalsすべてのを追加のに格納したい場合はvector、zoo 全体をラップすることで実行できます。

class Zoo
{
private:
    std::vector<std::shared_ptr<Animal>> animals;
    std::vector<std::shared_ptr<Dog>> dogs;
    // other vectors
public:
    void AddDog(const Dog& d)
    {
        std::shared_ptr<Dog> pD = std::make_shared<Dog>(d);
        dogs.push_back(pD);
        std::shared_ptr<Animal> pA = std::static_pointer_cast<Animal>(pD);
        animals.push_back(pA);
    }
};

メモリに格納するポインターの数は 2 倍になりますが、ポインターはかなり安価です。次に、毎回キャストを行う必要なく、動物園全体または個々の動物の種類を渡すことができます。

于 2013-10-09T23:47:58.123 に答える