创建`container&lt;的STL容器container <base />&gt;`

时间:2013-10-09 23:39:44

标签: c++ stl containers

有办法吗

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&amp; StripedDogs,自然地在向量或列表中,

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

然而,你希望迭代所有的动物,所以你希望抨击所有狗的参考。猫集合成一个对象,

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 个答案:

答案 0 :(得分:2)

你正在看这样的事情,但不太确定。如果它不是你所需要的,请告诉我,我会放弃它以支持那些有所帮助的人。输出演示虚拟feed()操作是否适当地执行其业务。将潜在的可变参数包安排到该函数中需要一些时间来烹饪一段时间,我甚至不确定它是否可行。

但这应该让你接近。

#include <iostream>
#include <algorithm>
#include <type_traits>
#include <vector>

// base. enforces inheritance by SFINAE
template<typename Base, typename T, template<typename, typename...> class V>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    for (auto p : vec)
        (p->*func)();
}

// chain.
template<typename Base, typename T, template<typename, typename...> class V, typename... Args>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
invoke(void (Base::*func)(), const class V<T*>& vec, Args... args)
{
    invoke(func, vec);
    invoke(func, args...);
}

int main()
{
    struct Animal
    {
        virtual void feed()
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
    } ;

    struct Dog : public Animal
    {
        void feed()
        {
            std::cout << __PRETTY_FUNCTION__ << std::endl;
        }
    } ;
    struct StripedDog : public Dog {};
    struct Cat : public Animal {};
    struct SpottedCat : public Cat {};

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

    Dog dog;
    doggies.push_back(&dog);

    StripedDog sdog;
    stripedDoggies.push_back(&sdog);

    Cat cat;
    catties.push_back(&cat);

    invoke(&Animal::feed, doggies, stripedDoggies, catties, spottedCatties);

    return 0;
}

<强>输出

typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Dog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = StripedDog, V = vector]
virtual void main()::Dog::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = Cat, V = vector]
virtual void main()::Animal::feed()
typename std::enable_if<std::is_base_of<Base, T>::value>::type invoke(void (Base::*)(), const class V<T *> &) [Base = Animal, T = SpottedCat, V = vector]

很抱歉不得不向右滚动才能看到漂亮的打印类型,但是它们很有说服力,应该看看它是如何工作的。请注意,DogStripedDog容器都会正确触发Dog::feed()成员,而Cat容器会正确触发Animal::feed()基本成员,因为它不提供覆盖。< / p> 祝你好运,我希望它有所帮助。

答案 1 :(得分:0)

一般来说,如果您正在做这样的事情,您可以将它们全部存储在一个载体中:

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中,可以通过包装整个动物园来实现:

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 :(得分: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 );

答案 3 :(得分: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);
    }
};