使用可变模板和运行时索引构建 iterator_range

Constructing an iterator_range with variadic templates and runtime indices

我有一组大小相同的向量,我想为用户提供一个接口来获取这些向量子集的迭代器范围。

以下示例显示了 getRange 中有问题的行:它的想法是接收一堆类型(指定向量的类型)和同样多的索引(指定向量的位置)。代码可以编译,但问题是 i++ 永远不会按预期执行,即调用总是只有 i(等于 0)。如果用户尝试获取不同的类型,这也会通过 boost::get 导致运行时错误。

这可能是一个众所周知的问题。有什么巧妙的解决方案?

#include <vector>
#include <boost/variant.hpp>
#include <boost/range/combine.hpp>

template <typename... T>
struct VectorHolder
{
    template<typename X>
    using Iterator = typename std::vector<X>::const_iterator;

    std::vector<boost::variant<std::vector<T>...> > vecs_;

    template <typename X>
    auto begin(int idx) const {
        return boost::get<std::vector<X> >(vecs_.at(idx)).cbegin();
    }

    template <typename X>
    auto end(int idx) const {
        return boost::get<std::vector<X> >(vecs_.at(idx)).cend();
    }
};

template <typename... T, typename VectorHolder>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx)
{
    assert(sizeof...(T) == idx.size());
    // Fetch a boost::iterator_range over the specified indices
    std::size_t i = 0;
    std::size_t j = 0;

    // PROBLEM: i and j not incremented as intended
    return boost::combine(
        boost::iterator_range<VectorHolder::Iterator<T>>(
            vh.begin<T>(idx[i++]), vh.end<T>(idx[j++]))...);
}

int main()
{
    VectorHolder<bool, int, double> vh;
    vh.vecs_.push_back(std::vector<int>(5, 5));
    vh.vecs_.push_back(std::vector<bool>(5));
    vh.vecs_.push_back(std::vector<double>(5, 2.2));
    vh.vecs_.push_back(std::vector<int>(5, 1));

    const std::vector<int> idx = { 0, 3 };
    for (auto t : getRange<int, int>(vh, idx))
    {
        std::cout << t.get<0>() << " " << t.get<1>() << "\n";
    }
}

std::index_sequence 帮助:

template <typename... Ts, typename VectorHolder, std::size_t ... Is>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx, std::index_sequence<Is...>)
{
    assert(sizeof...(Ts) == idx.size());

    return boost::combine(
        boost::iterator_range<typename VectorHolder::template Iterator<Ts>>(
            vh.template begin<Ts>(idx[Is]), vh.template end<Ts>(idx[Is]))...);
}

template <typename... Ts, typename VectorHolder>
auto getRange(const VectorHolder& vh, const std::vector<int>& idx)
{
    return getRange<Ts...>(vh, idx, std::index_sequence_for<Ts...>());
}

Demo