Reputation: 20311
I have this:
size_t n = 100;
std::vector<std::vector<foo>> v(n);
The count of the sub vectors
is dynamic but known. However, the number of items in each vector
is not known but I have an estimation about it so I want to reserve
the sub vectors
before start pushing back into them. What I am currently doing is:
size_t estimated_size = 1000;
for (auto& sub_vector: v){
sub_vector.reserve(estimated_size);
}
Is there a better way? Like doing it while constructing?
P.S. This is not an option:
size_t n = 100;
size_t estimated_size = 1000;
std::vector<std::vector<foo>> v(n, std::vector<foo>(estimated_size));
I just want to reserve without constructing because foo
is costy to be constructed twice.
Upvotes: 10
Views: 215
Reputation: 304132
Piggy-packing off of Yakk's answer here in a way that doesn't really involve writing my own code. With Ranges-v3, we can do this directly by just building a range of vector
s of the correct capacity. I also find this pretty easy to read:
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;
});
Upvotes: 1
Reputation: 275966
Here is a quick countdown iterator:
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);
}
};
a half-assed range class:
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)}; };
and then we can count:
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)};
}
use:
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() );
here we create a countdown "input" iterator that runs for 100 iterators. Each time you dereference it it returns a vector with m
capacity.
Upvotes: 2
Reputation: 602
If you really want to do it while constructing the vector
you could use the constructor that takes two iterators and provide your own custom iterator. Dereferencing the iterator would create a vector reserve it and then return it:
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));
But I wouldn't expect it to be faster than a loop and I don't think it is more readable either.
Upvotes: 3