[Bug libstdc++/68350] std::uninitialized_copy overly restrictive for trivially_copyable types

redi at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Tue Feb 5 13:53:00 GMT 2019


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68350

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |missed-optimization
   Last reconfirmed|2015-11-14 00:00:00         |2019-2-5

--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
To summarise:

The original report is wrong, we cannot use trivially-copyable there. The point
of the condition is to decide whether to use std::copy, because for
trivially-copyable types that is optimized to memmove. But unlike
std::uninitialized_copy, std::copy calls no constructors, so the type must be
trivially default constructible, and if an exception is thrown, it calls no
destructors, so the type must be trivially destructible as well. So we can only
use the memmove optimization for types which are trivially-copyable (which
implies trivially destructible) and trivially-default-constructible. That's a
trivial type, so __is_trivial is the right check.

The reason this bug is still open is that the other part of the condition is
overly-restrictive: the is_assignable check means that we don't optimize to
memmove for this case:

#include <memory>
struct Y {
  Y() = default;
  Y(const Y&) = default;
  Y& operator=(const Y&) = delete;
};
static_assert(std::is_trivially_copyable<Y>::value, "");

int main()
{
  alignas(Y) unsigned char buf[sizeof(Y)];
  Y y;
  std::uninitialized_copy(&y, &y+1, (Y*)buf);
}

This could be optimized to memmove. However, because std::copy would be
ill-formed (because std::copy does an assignment), we cannot use it, and do an
explicit copy construction using placement new. This is a missed-optimization.


More information about the Gcc-bugs mailing list