将容器视为一体

View containers as one

本文关键字:      更新时间:2023-10-16

条件是:

// 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&
  • (+)仅依赖于CCD_ 2=>无升压&编译速度更快
  • (+)不受限制(例如,可以使用擦除删除)
  • (?)不确定,但看起来msvc2015也可以编译
    注意:
    有大量的for_each元组算法。我拿了这个:
    http://pastebin.com/6e8gmZZA

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

以下代码使用Luc Dantons可变联接实现:

#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

实例

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

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

我希望这是一个典型的"高级C++"编程课上的家庭作业。

相关文章:
  • 没有找到相关文章