3

给定 myvector.start() 和 myvector.end() 我想在不复制数据的情况下创建 myvector 的只读子集

这可能吗,怎么做?

#include <iostream>
#include <vector>

using namespace std;

template <class T> void print_vector(const vector<T> &v) {
    for(size_t i = 0; i < v.size(); ++i) std::cout << v[i] << " ";
    std::cout << std::endl;
}

int main() {

首先,我创建向量。

    vector<double> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    print_vector(data); // 1 2 3 4 5 6 7 8 9 10

然后我想要一个子集。但我认为这是一个副本。

    // Does this make a copy or not? I don't want to make a copy.
    const vector<double> subset1(data.begin() + 3, data.end() - 3);
    print_vector(subset1); // 4 5 6 7

这种方法怎么样?

    // Another approach. Questions:
    // - Would something like this be a good alternative if I really don't want a copy?
    // - How would the performance of this be compared to a normal STL container?
    // - Is there an existing implementation of a container that does something like this, maybe a boost class?
    class SubsetType {
    public:
        SubsetType(const vector<double>::iterator &start, const vector<double>::iterator &end) { begin_ = start; end_ = end; }

        vector<double>::iterator begin() { return begin_; }
        vector<double>::iterator end() { return end_; }

        double operator[](vector<double>::size_type i) { return *(begin_ + i); }

        vector<double>::size_type size() { return end_ - begin_; }

    private:
        vector<double>::iterator begin_, end_;
    };

    SubsetType subset2(data.begin() + 3, data.end() - 3);
    for(size_t i = 0; i < subset2.size(); ++i) std::cout << subset2[i] << " ";
    std::cout << std::endl; // 4 5 6 7

或者是声明所有函数的解决方案,例如 f(const vector::iterator &start, const vector::iterator &en)。STL 算法就是这样做的,对吧?(但通用)

出口

    std::cout << "Bye!" << std::endl;
    return 0;
}
4

5 回答 5

2

只需使用迭代器(适当的 const 迭代器)。

如果您真的对到处传递开始/结束对感到过敏,请考虑使用Boost.Range或类似的东西:它可以让您将这[start,end)对组合成一个对象。

于 2012-10-11T22:21:47.853 回答
1

如果您不想要副本,请遵循与 std:: 算法库相同的习语并使用开始和结束迭代器。迭代器重量轻,不承担复制操作的成本。

于 2012-10-11T22:19:33.580 回答
1

在 C++11 中,所有容器都获得cbegin()andcend()函数,这允许您const_iterator甚至从非常量容器创建 s。之后,只需修改您的算法以接受开始和结束迭代器,就完成了。

print_range(data.cbegin()+3, data.cend()-3);

如果你被 C++03 卡住了,你可以使用一个光转发函数,将任何对象转换成它的const版本:

template<class T>
T const& as_const(T const& v){ return v; }

接着:

print_range(as_const(data).begin()+3, as_const(data).end()-3);
于 2012-10-11T22:21:26.070 回答
1

或者是声明所有函数的解决方案,例如 f(const vector::iterator &start, const vector::iterator &en)。

是的。

STL 算法就是这样做的,对吧?(但通用)

是的。一般也这样做。标准库算法是一个很好的建模示例。

于 2012-10-11T22:21:49.153 回答
1

Boost Range 将接近您的预期。

下面的示例显示了一个示例

  • 使用切片适配器创建向量的子范围“视图”(无副本)
  • 表明修改原始(基础)矢量元素会导致该视图中的数据发生变化
  • 用来make_iterator_range做大致相同的事情。

请注意,适配器是比简单的迭代器范围更灵活的抽象。

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>

using namespace boost::adaptors;

int main(int argc, const char *argv[])
{
    std::vector<int> v { 0, 1, 2, 3, 4, 5, 6 };

    auto output = std::ostream_iterator<int>(std::cout, ",");

    auto&& slice = v | sliced(2, 5);

    boost::copy(slice, output); 
    std::cout << '\n';

    v[3] += 10;

    boost::copy(slice, output); 
    std::cout << '\n';

    /// Alternative without adaptors:

    auto range = boost::make_iterator_range(v.begin()+3, v.end());

    boost::copy(range, output); 
    std::cout << '\n';
}

在http://liveworkspace.org/code/5be869c15f534b6161e61c392c181f2d上现场观看

执行输出:

2,3,4,
2,13,4,
13,4,5,6,
于 2012-10-11T22:22:24.363 回答