首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >创建`container< container<Base> >的STL容器

创建`container< container<Base> >的STL容器
EN

Stack Overflow用户
提问于 2013-10-09 23:39:43
回答 4查看 181关注 0票数 2

有没有办法

代码语言:javascript
复制
container< container<Base> >

当您有一堆希望保持在一起的container<Derived>(迭代器?)

下面是一个具体的例子。

说你有

代码语言:javascript
复制
struct Animal { } ;
struct Dog : public Animal { } ;
struct StripedDog : public Dog { } ;
struct Cat : public Animal { } ;
struct SpottedCat : public Cat { } ;

您希望将猫、SpottedCats、Dogs和StripedDogs的集合保存在向量或列表中,

代码语言:javascript
复制
vector<Dog*> doggies ;
vector<StripedDog*> stripedDoggies ;
vector<Cat*> catties ;
vector<SpottedCat*> spottedCatties ;

但是,您希望遍历所有的动物,因此您希望将对所有狗和猫集合的引用转换为一个对象,

代码语言:javascript
复制
vector< vector<Animal *>* > zoo ;

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

所以现在你可以

代码语言:javascript
复制
feed( zoo ) ;

当然,这不能编译。猫和狗的媒介不是vector<Animal*>,而是它们具体类型的载体。在不保留冗余列表和不丢失具体类型信息的情况下(即不像在vector<Animal*> stripedDoggies中那样使用基类型Animal*列表),是否有一种方法可以实现C++的等效行为?

EN

回答 4

Stack Overflow用户

发布于 2013-10-10 00:24:11

我想你是在看这样的东西,但不太确定。如果它不接近你所需要的,请告诉我,我会放弃它,以求帮助那些需要的人。输出演示虚拟feed()操作适当地执行其业务。为该函数安排一个潜在的变量参数包将花费我一段时间来烹饪,我甚至不确定这是否可能。

但这应该能让你接近。

代码语言:javascript
复制
#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;
}

输出

代码语言:javascript
复制
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()基本成员,因为它不提供任何覆盖。

祝你好运,我希望这能帮上忙。

票数 2
EN

Stack Overflow用户

发布于 2013-10-09 23:47:58

通常,如果您正在执行这样的操作,您将将它们全部存储在一个向量中:

代码语言:javascript
复制
std::vector<std::shared_ptr<Animal>> animals;

如果Animal定义了一个feed方法,那么迭代它就意味着调用该函数:

代码语言:javascript
复制
animals[i]->feed();

如果要根据类型调用特定函数,则需要执行一些强制转换:

代码语言:javascript
复制
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中,可以通过包装整个动物园来实现:

代码语言:javascript
复制
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);
    }
};

它是存储在内存中的指针数量的两倍,但是指针相当便宜。然后你可以通过整个动物园,或个别动物类型,而不需要做每一次的铸造。

票数 0
EN

Stack Overflow用户

发布于 2013-10-09 23:54:22

由于您使用的是相当便宜的指针,所以您可能会这样做:

代码语言:javascript
复制
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

如果不想复制/合并向量,另一个选项是:

代码语言:javascript
复制
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 );了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19284660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档