Author: | Thorsten Ottosen |
---|---|
Contact: | thorsten.ottosen@dezide.com |
organizations: | Dezide Aps |
Date: | 2007-03-08 |
Number: | WG21/N2212 and J16/07-0072 |
Working Group: | Library |
The lack of in-place construction in standard sequences is normally not a big problem. However, for libraries that tries to squeeze out the maximum efficiency of C++, it is a big problem that standard sequences are overly encapsulated. Experts needs to be able to write optimally efficient code.
The following type of libraries will benefit from this proposal:
Even though move-semantics will help performance, it is still not going to lead to optimal performance because construction followed by move-construction is less efficient than construction alone. Furthermore, some types are no more efficient to move than to copy, and those types are not only builtin types.
The following code demonstrates how one may exception-safely fill a generic sequence with value from a serialization library:
template< class Sequence, class Archive > void read( Sequence& into, Archive& from ) { int size; from >> size; into.uninitialized_grow( size ); try { typename Sequence::iterator e = into.begin(); for( int i = 0; i != size; ++i, ++e ) { typedef typename Sequence::value_type T; // // Call placement new directly on // uninitialized buffer // new( &*e ) T( from ); } } catch( ... ) { into.uninitialized_shrink( size - i ); throw; } }
The following code demonstrates how one may trivially code the handy uninitialized_resize() function:
template< class Sequence > void uninitialized_resize( Sequence& seq, typename Sequence::size_type n ) { if( n > seq.size() ) seq.uninitialized_grow( n - seq.size() ); else if( n < seq.size() ) seq.uninitialized_shrink( seq.size() - n ); else ; assert( seq.size() == n ); }
One could imagine different ways to achieve the same. For example, to fill an std::vector<T> with uninitialized storage, we could add the following member:
template <class T, class A> class vector { ... template <class F> // F models functor(T* data, size_type n) vector(size_type n, F f); // initialize vector with f(data(), n) ... };
and then use it like:
struct dont_initialize { template <class T> void operator()(T*, size_t) const {} }; ... vector<double> v(1000, dont_initialize());
This has several drawbacks:
Instead an approach similar to the existing insert() member functions has been pursued.
Add the following members to the synopsis of the class templates basic_string, list , deque and vector:
iterator uninitialized_grow( size_type n ); void uninitialized_shrink( size_type n );
Add the following specification after the synopsis of the class templates basic_string, list , deque and vector:
iterator uninitialized_grow( size_type n );
void uninitialized_shrink( size_type n );
Many thanks to Christopher Kohlhoff and Matthias Troyer for their feedback.