Date: Sat, 25 Oct 2025 00:53:29 +0100
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 functionsT& 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. ExplT& operator = (std::construct_tag, const T& t) : T(t){ }T& operator = (std::construct_tag, T&& t) : T(std::move(t)){ }The only problemt is : how to detect that the object is being constructed by assignment i.e how to choose one of the above new special functions?I mean:/// T t1, t2; T* p; T* q = &t1; *p = t2; // must pick construct by copy assign *q = t2; // must pick regular copy assign///Over to you.ThanksSent from my Galaxy
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 functionsT& 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. ExplT& operator = (std::construct_tag, const T& t) : T(t){ }T& operator = (std::construct_tag, T&& t) : T(std::move(t)){ }The only problemt is : how to detect that the object is being constructed by assignment i.e how to choose one of the above new special functions?I mean:/// T t1, t2; T* p; T* q = &t1; *p = t2; // must pick construct by copy assign *q = t2; // must pick regular copy assign///Over to you.ThanksSent from my Galaxy
Received on 2025-10-24 23:53:34
