Сохранение суб-векторов при построении
У меня есть это:
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;
});