default assignment should be established by construction

112 views
Skip to first unread message

wka...@oath.com

unread,
Mar 23, 2018, 3:02:26 PM3/23/18
to ISO C++ Standard - Future Proposals
For a class C, if the copy constructor is not deleted or suppressed, the default copy assignment operator should be:

C & operator = (const C &that)
{
  if (&that != this)
  {
    this->~C();
    new (this) C(that);
  }
  return *this;
}

(with const omitted from the parameter if it is omitted in the copy constructor).

Likewise, if the move constructor is not deleted or surpressed, the default move assignment operator should be:

C & operator = (C &&that)
{
  if (&that != this)
  {
    this->~C();
    new (this) C(std::move(that));
  }
  return *this;
}

Hypothetically this could break existing code, but I can't think of a practical example of useful code it would break.

Nevin Liber

unread,
Mar 23, 2018, 3:04:38 PM3/23/18
to std-pr...@isocpp.org
Only if you live in an exception-free world... 
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Matt Calabrese

unread,
Mar 23, 2018, 3:17:15 PM3/23/18
to ISO C++ Standard - Future Proposals
Also, what happens if C is the base type of the object (this is troublesome regardless of the destructor being virtual or not)? Bonus points -- what if it has const or reference datamembers ;)

I do agree, though, that in some idealized world this could be a sensible default.

-- Matt Calabrese

Walt Karas

unread,
Mar 23, 2018, 3:33:40 PM3/23/18
to ISO C++ Standard - Future Proposals
I don't think there's a clean escape from suckland if you're directly calling the assignment operator of a base class.  I think the best option would be to call the un-overridden destructor.

Can you elaborate on the problem with const and reference members?

Todd Fleming

unread,
Mar 23, 2018, 8:21:52 PM3/23/18
to ISO C++ Standard - Future Proposals
On Friday, March 23, 2018 at 3:33:40 PM UTC-4, Walt Karas wrote:
Can you elaborate on the problem with const and reference members?

Let's say you have this:

struct C {
   
const int i;
   
...
};

void f(C& x)
{
   
int i1 = x.i;
    g
(x);
   
int i2 = x.i;
   
...
}

It is currently impossible for g() to modify x.i without invoking undefined behavior. This allows the compiler to assume i1 == i2. Under the proposed rule, g(x) could assign to x, changing x.i in the process. See also std::launder.

Todd

Message has been deleted

Walt Karas

unread,
Mar 23, 2018, 9:39:26 PM3/23/18
to ISO C++ Standard - Future Proposals
It seems that the current behavior (of suppressing the default assignment operator if there are const or reference data member) would have to be kept.

Nicol Bolas

unread,
Mar 23, 2018, 11:45:48 PM3/23/18
to ISO C++ Standard - Future Proposals
On Friday, March 23, 2018 at 9:39:26 PM UTC-4, Walt Karas wrote:
It seems that the current behavior (of suppressing the default assignment operator if there are const or reference data member) would have to be kept.

So... what exactly is the purpose of this proposal? That's the thing I don't really understand; what does this allow us to do now that we could not before?

Richard Hodges

unread,
Mar 24, 2018, 3:33:17 AM3/24/18
to std-pr...@isocpp.org
It is currently impossible for g() to modify x.i without invoking undefined behavior.

Good!! 

Modifying x.i should be as impossible as the compiler can make it.

If you need it to be mutable, surely don't mark it const. 


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/49d9bc4b-24fa-4bc1-afe1-98100fa81e8f%40isocpp.org.

Walt Karas

unread,
Mar 24, 2018, 8:37:34 AM3/24/18
to ISO C++ Standard - Future Proposals
It's a convenience.  It think it would be very rare that it would not result in the desired behavior for the assignment operators.

Edward Catmur

unread,
Mar 24, 2018, 8:42:47 PM3/24/18
to ISO C++ Standard - Future Proposals
But how would this be any more convenient than the current behavior of memberwise assignment?

Thiago Macieira

unread,
Mar 24, 2018, 11:34:14 PM3/24/18
to std-pr...@isocpp.org
On Sunday, 25 March 2018 08:42:47 CST Edward Catmur wrote:
> But how would this be any more convenient than the current behavior of
> memberwise assignment?

It would be a pessimisation for any type that indirectly uses pimpl.

struct C
{
TypeWithPimpl d;
};

When C::operator=() gets called, according to the OP, it would run:
if (&that != this)
{
this->~C();
new (this) C(that);
}
return *this;

That means it calls d->~D(); then new (&d) D(that->d);

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center



Nicol Bolas

unread,
Mar 25, 2018, 12:00:53 AM3/25/18
to ISO C++ Standard - Future Proposals
On Saturday, March 24, 2018 at 8:37:34 AM UTC-4, Walt Karas wrote:
On Friday, March 23, 2018 at 10:45:48 PM UTC-5, Nicol Bolas wrote:
On Friday, March 23, 2018 at 9:39:26 PM UTC-4, Walt Karas wrote:
It seems that the current behavior (of suppressing the default assignment operator if there are const or reference data member) would have to be kept.

So... what exactly is the purpose of this proposal? That's the thing I don't really understand; what does this allow us to do now that we could not before?

It's a convenience.

How is it more convenient? All you're doing is changing the code that the compiler effectively generates. So what exactly does it make more convenient? Please provide an example where one would have to code their programs in an inconvenient way which this feature could allow us a more convenient alternative.

It think it would be very rare that it would not result in the desired behavior for the assignment operators.

Well, Thaigo's Pimpl example pretty much ended that argument.

Walt Karas

unread,
Mar 25, 2018, 9:54:27 AM3/25/18
to ISO C++ Standard - Future Proposals
Because it also handles the case where the constructor is not the default.  For example, it's doing deep copying. 

Thiago Macieira

unread,
Mar 25, 2018, 10:09:26 AM3/25/18
to std-pr...@isocpp.org
On Sunday, 25 March 2018 21:54:27 CST 'Walt Karas' via ISO C++ Standard -
Future Proposals wrote:
> Because it also handles the case where the constructor is not the default.
> For example, it's doing deep copying.

Which is exactly the case I argue that the member-wise assignment is better.

Nicol Bolas

unread,
Mar 25, 2018, 11:18:10 AM3/25/18
to ISO C++ Standard - Future Proposals

But deep copy assignment is a different operation from deep copy construction. In deep copy assignment, you do not want to allocate a new object the way you would have to for deep copy construction. It's silly to deallocate the currently managed object just to create a new one.

Now yes, if you're managing some polymorphic type through a base class pointer, then you'll have to destroy and `clone` it either way. But that's hardly the only use case of deep copy semantics. There are plenty of deep copy types that know exactly what object they contain, and all of them will still have to provide an explicit copy/move assignment.

I don't think this case is common enough to create a language change. You can just write the explicit copy/move assignment code for those cases where it crops up.
Reply all
Reply to author
Forward
0 new messages