将容器视为一个

时间:2016-07-23 13:37:13

标签: c++ c++14

条件是:

// A hierarchy
struct Base {
    virtual void everyone_has_this() = 0;
};
struct DA : Base {
    void everyone_has_this() override {...}
};
struct DB : Base {
    void everyone_has_this() override {...}
    void only_db_has_this() {...}
};

// And some vectors
vector<DA> das (3);
vector<DB> dbs (2);

我希望能够使用类似的东西:

// Foreach all of them in one loop
for(Base& o : view_as_one<Base>(das, dbs)) {
   o.everyone_has_this();
}
// Or just several types of them
for(DB& o : dbs) {
   db.only_db_has_this();
}

问题是:有可能吗?
如果没有,一个循环的其他方法是什么,而不是每个容器? 重要的是,我不想摆脱隔离存储的重要性。

如果我使用单个基本指针容器,那么dynamic_cast就可以工作,但是它会涉及在每次迭代时检查类型,目标是将所有对象存储在有名的存储中。

编辑:替代解决方案如果提升不是选项或视图不够的话 虽然我喜欢m.s.的解决方案,因为他完全实现了我要求的界面,我发现我想要的这个界面实际上并不灵活(不能使用擦除 - 删除)。 所以这是一个不同的方法:

for_each_in_tuple(std::tie(das, dbs), [](auto& cont){
   for(auto& obj : cont) {
       // do what you want
   }
   // or even
   cont.erase(std::remove_if(begin(cont), end(cont), [](auto& obj) {
       // also do what you want
   }, end(cont));
});
  • ( - )不是很漂亮
  • (+)迭代器不必检查它属于哪个范围(更快)
  • (+)即使没有虚拟方法也可以使用(因为auto&)。
  • (+)仅依赖于<tuple> =&gt;没有提升&amp;编译得更快。
  • (+)不受限制(例如可以使用擦除 - 移除)
  • (?)不确定,但看起来msvc2015也可以编译 注意:
    有很多for_each元组算法。我拿了这个:
    http://pastebin.com/6e8gmZZA

2 个答案:

答案 0 :(得分:1)

可以使用boost::transform_iteratorboost::join来实现。

以下代码使用Luc Dantons variadic join实现:

#include <vector>
#include <tuple>
#include <iostream>
#include <utility>

#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/join.hpp>
#include <boost/range/iterator_range.hpp>


namespace ns {

// for ADL purposes
using std::begin;
using std::end;

struct join_type {
    template<class C>
    auto operator()(C&& c) const
    -> decltype(boost::make_iterator_range(begin(c), end(c)))
    {
        return boost::make_iterator_range(begin(c), end(c));
    }

    template<typename First, typename Second, typename... Rest>
    auto operator()(First&& first, Second&& second, Rest&&... rest) const
    -> decltype( (*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...) )
    {
        return (*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...);
    }
};

constexpr join_type join {};

} // ns


template <typename T>
struct ReturnTRef
{
    T& operator()(T& x) const { return x;};
};

template <typename T, typename Tuple, std::size_t... Indices>
auto view_as_one_impl(Tuple&& tuple, std::index_sequence<Indices...>)
{
    ReturnTRef<T> returnTRef;
    return ns::join(boost::make_iterator_range(boost::make_transform_iterator(begin(std::get<Indices>(tuple)), returnTRef), boost::make_transform_iterator(end(std::get<Indices>(tuple)), returnTRef))...);
}

template <typename B, typename... Args>
auto view_as_one(Args&&... args)
{
    return view_as_one_impl<B>(std::forward_as_tuple<Args...>(args...), std::index_sequence_for<Args...>{});
}


struct Base {virtual ~Base(){}; virtual void talk() = 0;};
struct DA : Base {void talk() override { std::cout << "DA" << std::endl;} };
struct DB : Base {void talk() override { std::cout << "DB" << std::endl;} };


int main()
{
    std::vector<DA> das(3);
    std::vector<DB> dbs(2);

    for(Base& x : view_as_one<Base>(das, dbs))
    {
        x.talk();
    }
}

<强>输出

DA
DA
DA
DB
DB

live example

答案 1 :(得分:0)

是的,有可能。只需编写一些代码即可。

只需实现view_as_one即可满足C ++库容器的要求;或至少实现此功能所需的最低要求。它的构造函数存储对成分,底层容器的引用。将其iterator类实现为按顺序迭代每个组成容器。实施view_as_one<T>::begin()view_as_one<T>::end()。完成。

我希望将此作为典型的高级C ++&#34;中的家庭作业。编程课。