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 ;

但是,您希望遍历所有动物,因此您希望将对所有狗和猫集合的引用猛烈抨击到一个对象中,

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

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()基础成员,因为它没有提供覆盖。

祝你好运,我希望它有所帮助。

于 2013-10-10T00:24:11.857 回答
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的 s 中,你可以通过包装整个 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);
    }
};

它使您存储在内存中的指针数量增加了一倍,但指针相当便宜。然后,您可以通过整个动物园或单个动物类型,而无需每次都进行铸造。

于 2013-10-09T23:47:58.123 回答
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 回答