在构造时保留子向量

时间:2016-03-01 09:15:37

标签: c++ c++11 vector

我有这个:

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);
}

有更好的方法吗?喜欢在构建时这样做吗?

P.S。这不是一个选择:

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

我只想保留而不构造,因为foo是构建两次的costy。

3 个答案:

答案 0 :(得分:3)

如果确实希望在构造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.

答案 1 :(得分: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() );

这里我们创建一个倒计时&#34;输入&#34;运行100个迭代器的迭代器。每次取消引用它时,它都会返回一个容量为m的向量。

答案 2 :(得分:1)

Yakk's的小猪包装在这里以一种不真正涉及编写我自己的代码的方式回答。使用Ranges-v3,我们可以通过构建一系列正确容量的vector来直接执行此操作。我也觉得这很容易阅读:

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;
    });