【问题标题】:Reserving sub vectors while constructing构造时保留子向量
【发布时间】:2016-06-13 15:02:00
【问题描述】:

我有这个:

size_t n = 100;
std::vector<std::vector<foo>> v(n);

sub vectors 的计数是动态但已知的。但是,每个vector 中的项目数量未知,但我对此有一个估计,所以我想在开始推回它们之前先reserve sub vectors。我目前正在做的是:

size_t estimated_size = 1000;
for (auto& sub_vector: v){
   sub_vector.reserve(estimated_size);
}

有没有更好的方法?喜欢一边施工一边做?

附:这不是一个选项:

size_t n = 100;
size_t estimated_size = 1000;
std::vector<std::vector<foo>> v(n, std::vector<foo>(estimated_size));

我只想保留而不构建,因为foo 构建两次成本很高。

【问题讨论】:

  • 如果不构造该数量的对象,就无法在构造时保留/预置vector。不幸的是。
  • @HumamHelfawi 老实说,我会选择循环/reserve()。真的没有那么糟糕。
  • @BoBTFish:抱歉,我的评论措辞不准确。编辑它。 (我的意思是 在施工时预先调整大小。
  • 这是一个有趣的问题,但是当你说“更好”时,你到底是什么意思?
  • 我刚刚测试了在返回隐式转换之前制作一个包装类来进行预先保留,但似乎复制/覆盖对象会忘记任何预分配

标签: c++ c++11 vector


【解决方案1】:

如果您真的想要在构造 vector 时这样做,您可以使用带有两个迭代器并提供您自己的自定义迭代器的 constructor。取消引用迭代器将创建一个向量保留它然后返回它:

class VectorReserveItr : public std::iterator<std::input_iterator_tag, foo> {
  size_t i;
  size_t capacity;
public:
  VectorReserveItr(size_t i, size_t capacity) : i(i), capacity(capacity) {}
  VectorReserveItr& operator++() { ++i; return *this; }
  bool operator!=(const VectorReserveItr& rhs) { return i != rhs.i; }
  std::vector<foo> operator*() {
      std::vector<foo> ret;
      ret.reserve(capacity);
      return ret;
  }
};

std::vector<std::vector<foo>> v(VectorReserveItr(0, 1000), VectorReserveItr(100, 1000));

但我不希望它比循环更快,我也不认为它更具可读性。

Live demo.

【讨论】:

    【解决方案2】:

    这是一个快速倒计时迭代器:

    template<class F,
      class T=std::result_of_t<F const&(std::size_t const&)>
    >
    struct countdown_iterator:
      std::iterator<
        std::input_iterator_tag,
        T,
        std::ptrdiff_t,
        T*,
        T
      >
    {
      using self=countdown_iterator;
      std::size_t count_down = 0;
      F f;
      T operator*() const {
        return f(count_down);
      }
      self& operator++() {
        --count_down;
        return *this;
      }
      self operator++(int) {
        auto result = *this;
        ++(*this);
        return result;
      }
      friend bool operator==(self const& lhs, self const& rhs) {
        return lhs.count_down == rhs.count_down;
      }
      friend bool operator!=(self const& lhs, self const& rhs) {
        return !(lhs==rhs);
      }
    };
    

    半途而废的范围类:

    template<class It>
    struct range {
      It b, e;
      It begin() const { return b; }
      It end() const { return e; }
      bool empty() const { return begin()==end(); }
      decltype(auto) front() const { return *begin(); }
      range():b(),e() {}
      range(It s, It f):b(s), e(f) {}
      range(range const&)=default;
      range& operator=(range const&)=default;
      ~range() = default;
    
      template<class C,
        class=std::enable_if_t<!std::is_same<std::decay_t<C>, range>>
      >
      range( C&& c ):
        range(std::begin(std::forward<C>(c)), std::end(std::forward<C>(c)))
      {}
    };
    template<class It>
    range<It> make_range( It b, It e ) { return {std::move(b),std::move(e)}; };
    

    然后我们可以数数:

    template<class F,
      class dF=std::decay_t<F>,
      class It=countdown_iterator<dF>
      class R=range<It>
    >
    R countdown( std::size_t N, F&& f ) {
      countdown_iterator e( N, f ):
      countdown_iterator b( N, std::forward<F>(f) );
      return {std::move(b),std::move(e)};
    }
    

    使用:

    size_t n = 100;
    
    size_t m = 1000;
    auto src = countdown(
      n,
      [m](auto&&){ std::vector<foo> v; v.reserve(m); return v; }
    );
    std::vector<std::vector<foo>> v;
    v.reserve(100);
    v.insert(v.end(), src.begin(), src.end() );
    

    在这里,我们创建了一个可运行 100 个迭代器的倒计时“输入”迭代器。每次取消引用它时,它都会返回一个具有m 容量的向量。

    【讨论】:

    • 为什么不std::vector&lt;std::vector&lt;foo&gt;&gt; v(src.begin(), src.end());
    • @barry 由于引用问题,无法编写随机访问输入迭代器(没有后备存储)。所以不能指望向量正确地预先调整大小。所以不得不手动预约。 Simpky 定义 - 可能使其适用于某些编译器,尽管具有值引用类型,但同样声称是随机访问,但我很谨慎。
    【解决方案3】:

    Yakk's 回答这里的小猪包装,实际上并不涉及编写我自己的代码。使用Ranges-v3,我们可以直接通过构建具有正确容量的vectors 范围来做到这一点。我也觉得这很容易阅读:

    std::vector<std::vector<int>> v = 
        view::ints(0, 100)
        | view::transform([](int ) {
            std::vector<int> sub_v;
            sub_v.reserve(100);
            return sub_v;
        });
    

    【讨论】:

      猜你喜欢
      • 2013-03-21
      • 2013-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 2012-03-08
      相关资源
      最近更新 更多