Base on specialized.algorithms.general and uninitialized.copy I don't find any formal requirements that the destination range must be uninitialized. As such, we can consider the effect of std::uninitialized_copy
which is defined as equivalent to the follow (excluding the implied exception safety boilerplate code) :
for (; first != last; ++result, (void) ++first)
::new (voidify(*result))
typename iterator_traits<NoThrowForwardIterator>::value_type(*first);
We can conclude that std::uninitialized_copy
does not call any destructor or otherwise cares about what was previously located in the destination range. It simply overwrites it, assuming it is uninitialized.
To figure out what this means in terms of correctness, we can refer to basic.life
A program may end the lifetime of an object of class type without invoking the destructor, by reusing or releasing the storage as described above. In this case, the destructor is not implicitly invoked and any program that depends on the side effects produced by the destructor has undefined behavior.
This however uses the loosely defined notion of "any program that depends on the side effects of". What does it mean to "depend on the side effects of"?
If your question was about overwriting vectors of char
or int
there would be no problem, as these are not class types and do not have destructors so there can be no side effects to depend on. However std::string
's destructor may have the effect of releasing resources. std::basic_string
may have addition, more directly observable side effects if a user defined allocator is used. Note that in the case of a range containing non-class type elements std::uninitialized_copy
is not required. These elements allow for vacuous initialization and simply copying them to uninitialized storage with std::copy
is fine.
Since I don't believe it is possible for the behavior of a program to depend on the release of std::string
's resources, I believe the code above is correct in terms of having well defined behavior, though it may leak resources. An argument could be made that the behavior might rely on std::bad_alloc
eventually being thrown, but std::string
isn't strictly speaking required to dynamically allocate. However, if the type of element used had side effects which could influence the behavior, and the program depended on those effects, then it would be UB.
In general, while it may be well defined in some cases, the code shown violates assumptions on which RAII is based, which is a fundamental feature most real programs depend on. On these grounds std::uninitialized_copy
should not be used to copy to a range which already contains objects of class type.