Date: Sat, 25 Oct 2025 12:40:18 +0100
On Sat, 25 Oct 2025 at 00:53, organicoman via Std-Proposals <
std-proposals_at_[hidden]> 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&)".
>
> T*he 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.
std-proposals_at_[hidden]> 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&)".
>
> T*he 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.
Received on 2025-10-25 11:40:38
