Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

uninitialized move in std::vector

I use visual studio 2010 and its stl implementation is from dinkumware

I noticed that when internal buffer of vector grows, there is a _Uninitialized_move is called. which is much like std::uninitialized_copy.

  1. for scalar type, it eventually forwards call to memmove
  2. for non-scalar type, it will call copy constructor of contained type.

I know that for non-scalar type, bit-wise coping isn't safe, but I am get confused in this case, because the old objects will be destroyed very soon. it looks like that bit-wise coping for all types is safe, and hence we only need free raw buffer after _Uninitialized_move. this will save lots of copy constructor and destructor for non-trivial objects.

so, is it safe to just move contents for non-scalar type?

like image 408
Chang Avatar asked Jan 18 '23 20:01

Chang


2 Answers

If you're completely sure that objects you move don't have pointers to each other (subobjects of those included) - then just moving contents is okay. However you can't know that unless you know how the type is designed inside. One exception is that if the type is not larger than a pointer (sizeof(Type) <= sizeof( void* )), then it is very unlikely to have pointers to objects in the same container, so usually it can be just moved.

like image 120
sharptooth Avatar answered Jan 24 '23 21:01

sharptooth


In C++11, there are many traits to know whether it is safe to perform bit operations or not.

In your case, I think you should use std::is_trivially_move_constructible<T>. Those traits are implemented using compiler intrinsics (non-portable, that's why it's in the Standard Library), but they are themselves portable.

Therefore, the code should be akin to:

template <typename T>
void move_to(T* storage, T* begin, T* end) {
  if (std::is_trivially_move_constructible<T>::value) {
    memmove(storage, begin, (end - begin) * sizeof(T));
  } else {
    for (; begin != end; ++begin, ++storage) {
      new (storage) T(std::move(*begin));
    }
  }
}

And the compiler will optimize the if at compilation time depending on whether the type is trivially move constructible or not and only leave the interesting branch.

like image 20
Matthieu M. Avatar answered Jan 24 '23 20:01

Matthieu M.