Consider I have a class A
that is neither copy constructible nor copy assignable, but has any-like constructor A(auto &&)
. Can I create copies of std::vector<A>
then?
#include <concepts>
#include <vector>
struct A {
A() {}
A(const A&) = delete;
A(auto &&) {}
A & operator=(const A&) = delete;
};
static_assert(!std::copy_constructible<A>);
const std::vector<A> v(3);
auto w( v ); // fails only in libstdc++
In GCC with libstdc++
the compilation fails with rather long error message, where the essential part seems to be:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/stl_uninitialized.h:90:56: error: static assertion failed: result type must be constructible from input type
90 | static_assert(is_constructible<_ValueType, _Tp>::value,
| ^~~~~
However, both MSVC and Clang with libc++
accept the program just fine. Online demo: https://gcc.godbolt.org/z/vhdEGn5sv
Which implementation is correct here?
auto w( v )
is exactly the condition in [container.reqmts]:
Preconditions: T is Cpp17CopyInsertable into X
which means that:
the following expression is well-formed:
allocator_traits<A>::construct(m, p, v)
In the extant case, m
is an allocator of type std::allocator<A>
, p
is a pointer of type A*
, and v
is an expression that denotes an lvalue of type A
or const A
or an rvalue of type const A
.
This is equivalent to:
::new (voidify(*p)) A(v) // when v is lvalue of type A or const A
This fails when v
is an lvalue of type const A
, so the precondition is violated, so the program has undefined behavior and anything is valid.
In practice, the standard library has license to construct from A&
or const A&
and they make different choices. Constructing from A&
will by overload resolution choose the greedy template.