有办法吗
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 ++实现等效行为?
答案 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]
很抱歉不得不向右滚动才能看到漂亮的打印类型,但是它们很有说服力,应该看看它是如何工作的。请注意,Dog
和StripedDog
容器都会正确触发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);
}
};