Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reducing the size of a std::vector without a default constructor

Tags:

c++

stl

I have a templated class using a std::vector of it's template argument. The argument may not be default constructible. I want to reduce the size of the vector (cut it to a given size). Obviously

vec.resize( reduced_size );

...doesn't work as it requires a default constructor.

I could of course:

  1. create the default constructor for any used type (which requires me to add it when it might not be a good design choice)
  2. pass a default value to the function (useless clutter of the interface)
  3. pass a construction method to the template (also useless clutter)

While writing the question, I noticed that I can erase the elements from the vector up to the end:

vec.erase ( vec.begin() + position, vec.end() );

... however, I'm not sure if this will be as efficient as resize.

Is there an efficient way to reduce a vector's size without a default constructor?

C++11 solutions are acceptable.


EDIT: Seems that both MSVC and GCC implement shrinking resize as a erase call, so that answers my performance question.

like image 694
Kornel Kisielewicz Avatar asked Jun 12 '13 00:06

Kornel Kisielewicz


2 Answers

In my implementation, it looks like we have (with a few simplifications):

void std::vector<T,A>::resize(size_type __new_size)
{
    if (__new_size > size())
        _M_default_append(__new_size - size());
    else if (__new_size < size())
        _M_erase_at_end(begin() + __new_size);
}

auto std::vector<T,A>::erase(iterator __first, iterator __last) -> iterator
{
    if (__first != __last)
    {
        if (__last != end())
            _GLIBCXX_MOVE3(__last, end(), __first);
        _M_erase_at_end(__first + (end() - __last));
    }
    return __first;
}

where _M_... are private member functions. You really want the effects of _M_erase_at_end. I would guess it would be hard or impossible for a compiler to optimize the _M_default_append call out of v.resize(sz), but relatively easy to notice in v.erase(iter, v.end()) that __last == end() and optimize away the _GLIBCXX_MOVE3 and the + (end() - __last). So erase() could very well be more efficient than resize() here.

I would expect most implementations to be a similar story: a few simple if tests, and then calling some identical method to call destructors for elements at the end.

like image 183
aschepler Avatar answered Oct 10 '22 05:10

aschepler


Your idea to use erase is the right route. To reduce the amount of confusion, I would write a container based algorithm:

template<typename Container>
Container&& reduce_size( Container&& c, std::size_t amount ) {
  amount = std::min( amount, c.size() ); // paranoid!
  c.erase( end(c)-amount, end(c) );
  return std::forward<Container>(c); // I like my container-algorithms to pass through
}

which will be as fast as your erase implementation (well, one more branch and check).

Use:

std::vector< Foo > blah;
blah.emplace_back( 7 );
reduce_size( blah, 10 );
like image 2
Yakk - Adam Nevraumont Avatar answered Oct 10 '22 04:10

Yakk - Adam Nevraumont