On Sat, 25 Oct 2025 at 00:53, organicoman via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

Hello,
Given a container C of value_type T, the expected behavior when applying an action on the container is to see that action distributed over its elements. 
-Copying the container->copies each elem.
-Destroying the container->destroys each elem.
-Moving the container-> moves each elem.
(Moving here in the sense moving from one memory location to another one).
By extrapolation:
-Applying an action-> applys it on each elem.
...
Yet there is one fundamental action that doesn't comply to this mental model:
Assignment !
When you assign one container to another, it is not guaranteed that all elements use Assignment operation.
C<T> v1;
v1 = v2; // or = std::move(v2);
Usually the implementation distributes copy-construction, or move-construction operation on each element instead of copy/move assignment operator. 
And sometimes you see a mixture of both (construction and assignment)
Let say you have:


#include <vector>
#include <iostream>
struct A{
  A(){ std::puts(__PRETTY_FUNCTION__);}
  A(const A&)
  { std::puts(__PRETTY_FUNCTION__);}
  A(A&&)
  { std::puts(__PRETTY_FUNCTION__);}
  A& operator=(const A&) = default;//elete;
  A& operator=(A&&)= default;//elete;
};

int main()
{
  std::vector<A> v1(5);
  std::vector<A> v2;
  std::puts("-----");
  v2 = v1;
  return 0; 
 }
######

Output:

A::A()
A::A()
A::A()
A::A()
A::A()
-----
A::A(const A&)
A::A(const A&)
A::A(const A&)
A::A(const A&)
A::A(const A&)


As you can see...
The mental model of distributing actions over each element is not respected for the copy assignment operation, instead of no log after the "-----" above,  we see "A::A(const A&)".

The proposal:
To keep the mental model of "action distribution over container elements",  i suggest adding the capability of "construction by assignment"
That is, we can construct an object directly by copy assignment or move assignment.

And the following snippet should not be UB.
///
T* p = (T*)malloc(sizeof(T));
T val;
*p = val; // UB here
///
for all T's either trivial or non trivial constructible.

One suggestion is to add 2 new special functions

T& operator = (std::construct_tag, const T&);
T& operator = (std::construct_tag, T&&);

Where the implementation just delegates the construction to the appropriate copy/move constructor. Expl
T& operator = (std::construct_tag, const T& t)
  : T(t)
{ }

T& operator = (std::construct_tag,  T&& t)
  : T(std::move(t))
{ }

How would this even help with the confusion you have?

This would still call the copy constructor, so if you're logging in your copy constructor and copy assignment operator, you'll still see that some elements in the vector were modified by copy assignment, and new elements were initialized by copy construction. So you'll still see "A::A(const A&)" for your example above!

So this doesn't even help with the one thing that you think it would be useful for.

So combined with being completely unimplementable (because in the general case the compiler can't tell whether assignment is to an existing object or to raw memory), this is just a terrible idea that doesn't even work.