2016-03-01 8 views
10

ho questo:Riservando vettori secondari, mentre la costruzione

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

Il conteggio del sub vectors è dinamico, ma conosciuta. Tuttavia, il numero di elementi in ogni vector non è noto, ma ho una stima su di esso, quindi voglio reserve il sub vectors prima di iniziare a spingere di nuovo in loro. Quello che sto facendo attualmente è:

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

C'è un modo migliore? Come farlo mentre costruisci?

P.S. Questa non è un'opzione:

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

voglio solo prenotare senza costruire perché foo è costy da costruire due volte.

+0

Penso che dipenda molto da come andrete a popolare i vettori interni. – LogicStuff

+0

@LogicStuff Potresti per favore chiarire un po '? –

+0

"Voglio solo prenotare senza costruire perché foo è costoso da costruire due volte." - Non memorizzare 'foo's direttamente? Forse ['std :: unique_ptr '] (http://en.cppreference.com/w/cpp/memory/unique_ptr) o ['std :: experimental :: opzionale '] (http: // en. cppreference.com/w/cpp/experimental/optional). – BoBTFish

risposta

3

Se davvero vuole farlo, mentre la costruzione del vector è possibile utilizzare il constructor che prende due iteratori e fornire il proprio iteratore personalizzato. Dereferencing l'iteratore creerebbe una riserva di vettore e poi restituirla:

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

Ma io non ci si aspetterebbe di essere più veloce di un ciclo e non credo sia più leggibile sia.

Live demo.

2

ecco un breve iteratore conto alla rovescia:

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

una classe gamma assed:

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

e poi possiamo contare:

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

uso :

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

qui creiamo un iteratore conto alla rovescia "input" che corre per 100 iteratori. Ogni volta che lo si chiama, restituisce un vettore con la capacità m.

+1

Perché non 'std :: vector > v (src.begin(), src.end()); '? – Barry

+0

@barry non può scrivere un iteratore di input di accesso casuale (senza backing store) a causa di un problema di riferimento. Quindi non ci si può aspettare che il vettore sia pre-dimensionato correttamente. Quindi hai prenotato manualmente. La definizione di '' 'Simpky potrebbe farla funzionare su alcuni compilatori, affermando in modo simile un accesso casuale nonostante abbia un tipo di riferimento di valore, ma io sono cauto. – Yakk

1

Piggy-packing off of Yakk's rispondi qui in un modo che in realtà non implica la scrittura del mio codice personale. Con Ranges-v3, possiamo farlo direttamente semplicemente creando un intervallo di vector s della capacità corretta. Trovo anche abbastanza facile da leggere:

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