https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122313
--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #3)
> Here's a C++17 test that doesn't rely on parenthesized init of aggregates:
>
> #include <vector>
> #include <memory_resource>
>
> void test1()
> {
> #if __cplusplus >= 202002L
> struct S { int i; };
> static_assert(std::is_constructible_v<S, int&>);
>
> std::vector<int> base_v;
> std::vector<S> v;
> v.insert(v.end(), base_v.begin(), base_v.end());
> #endif
> }
This should construct S(int) and insert that, not just do *pos = *first.
Constructing S(int) using aggregate-init is needed, we can't assume
is_convertible_v<int&, S> or is_assignable_v<S&, int&>.
> void test2()
> {
> struct S { explicit S(int) { } };
> static_assert(std::is_constructible_v<S, int&>);
>
> std::vector<int> base_v;
> std::vector<S> v;
> v.insert(v.end(), base_v.begin(), base_v.end());
> }
Same here, but constructing S(int) uses an explicit constructor.
> void test3()
> {
> struct S {
> S(int) { }
> void operator=(int) = delete;
> };
> static_assert(std::is_constructible_v<S, int&>);
>
> std::vector<int> base_v;
> std::vector<S> v;
> v.insert(v.end(), base_v.begin(), base_v.end());
> }
This time the constructor is implicit, but the assignment is deleted, so no
implicit conversion is attempted.
> void test4()
> {
> struct S {
> using allocator_type = std::pmr::polymorphic_allocator<S>;
> S() { }
> S(allocator_type) { }
> S(int) { throw; }
> S(int, allocator_type) { }
> S(const S&, allocator_type) { }
> };
> static_assert(std::is_constructible_v<S, int&>);
>
> std::vector<int> base_v(1);
> std::pmr::vector<S> v(1);
> v.reserve(2);
> v.insert(v.begin(), base_v.begin(), base_v.end());
> }
This one compiles, but does an implicit conversion from int& to S, which uses
the throwing constructor.
Cpp17EmplaceConstructible into vector using int& means constructing an S
properly using allocator_traits::construct, which would use S(int,
allocator_type) not S(int).
One more failing test:
void test5()
{
struct S
{
S() { }
S(const S&) { }
S& operator=(S&&) { return *this; }
};
S base_v[1];
std::vector<S> v;
v.insert(v.end(), base_v, base_v+1);
}
Here S meets all the requirements for vector::insert(const_iterator, Iter,
Iter), it is
C++17EmplaceConstructible into vector from S&, it is Cpp17MoveInsertable and
Cpp17MoveAssignable, and CPp17MoveSwappable. But we try to do a copy assignment
inside std::copy.