lundi 2 novembre 2015

Memmove, std::uninitialized_copy and gcc

std::uninitialized_copy copies into an uninitialized range of memory. This could be done using std::memmove for bitwise copyable types. I stepped through below example code in gdb (compiling with gcc 5.2.0). Thereby I observed that __is_trivial(Bar) is used to determine whether memmove may be used. It (correctly) evaluates to false as Bar has a non-trivial default constructor (cf. call to std::__uninitialized_copy<false>::__uninit_copy(...) in bits/stl_uninitialized.h line 123ff).

But why is __is_trivial even relevant to std::uninitialized_copy? According to Bjarne std::is_trivially_copy_constructible should be sufficient. Note that the latter evaluates to true in the example, ie. the memmove optimization is applicable.

I'm aware, that the standard does not require any specific implementation of std::uninitialized_copy. I'm just wondering why __is_trivial is favored even if std::is_trivially_copy_constructible is present as an applicable alternative to the gcc implementation?

Example Code:

#include <iostream>
#include <vector>
#include <type_traits>

struct Bar
{
   Bar () : v(17) {};
   Bar(Bar const &) = default;
   Bar(Bar &&) = default;
   Bar & operator=(Bar &&) = default;
   Bar & operator=(Bar const &) = default;
   int v;
};

int main() {
   std::cout
      << std::is_trivially_move_constructible<Bar>::value
      << " " << std::is_trivially_copy_constructible<Bar>::value
      << " " << std::is_trivially_copyable<Bar>::value
      << " " << std::is_trivial<Bar>::value
      << " " << __is_trivial(Bar) << std::endl;
   std::vector<Bar> v(10);
   Bar * vc = new Bar[10];
   std::uninitialized_copy(v.begin(), v.end(), vc);
   delete[] vc;
}

Example Output: 1 1 1 0 0

Aucun commentaire:

Enregistrer un commentaire