Сохранение суб-векторов при построении

У меня есть это:

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 стоит построить дважды.

Ответ 1

Если вы действительно хотите это сделать при построении vector, вы можете использовать конструктор , который принимает два итератора и предоставляет собственный пользовательский итератор, Разъявление итератора создало бы векторный резерв, а затем вернет его:

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

Но я бы не ожидал, что это будет быстрее, чем цикл, и я не думаю, что это более читаемо.

Живая демонстрация

Ответ 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.

Ответ 3

Piggy-упаковка Якка отвечает здесь таким образом, что на самом деле не нужно писать собственный код. С 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;
    });