Discussion Thread: DIP 1040--Copying, Moving, and Forwarding--Community Review Round 1

Walter Bright newshound2 at digitalmars.com
Thu Mar 18 09:39:22 UTC 2021


On 3/8/2021 5:21 AM, RazvanN wrote:
> On Monday, 8 March 2021 at 10:38:25 UTC, Walter Bright wrote:
>> On 3/8/2021 12:23 AM, RazvanN wrote:
>>> On Friday, 5 March 2021 at 12:19:54 UTC, Mike Parker wrote:
>>>>
> 
>>> Moreover, what happens in this case if we have a struct that's not an EMO, 
>>> but defines a move constructor?
>>
>> That means it does not have a Move Assignment Operator. It doesn't get EMO 
>> semantics. The Move Constructor section applies.
>>
> Even if the move assignment operator is implicitly generated? The DIP states: 
> "If a Move Assignment Operator is not defined for a struct that has a Move 
> constructor, a default Move Assignment Operator is defined and implemented as a 
> move for each of its fields, in lexical order."
> 
> Is it possible to have a non-EMO struct that defines solely a move constructor 
> or solely a move assignment operator? It seems like if you define one, you 
> implicitly get the other one.

That's a good point. Perhaps it should be an error to define only one.


>>> 2. What are the situations where a move constructor call is implicitly 
>>> inserted by compiler? This is not explicitly stated in the DIP and it is 
>>> rather confusing.
>>
>> It's in the Move Constructor section.
>>
>>
>>> For example, when an instance is passed by `move ref` is there a move 
>>> constructor call?
>>
>> No, because it is passed by ref.
> Ok, correct me if I am wrong, but it seems that if you define a move 
> constructor, you implicitly get a move assignment operator and viceversa. This 
> means that your struct becomes an EMO if you define one or the other. Once you 
> have an EMO struct, besides the trivial `S a = b` are there any other situations 
> where the move constructor may be called?

void g(S b) {
    S a = b; // calls copy constructor (not last use of b)
    S c = b; // calls move constructor (last use of b)
}


  It seems that EMOs are always passed
> by reference. If that is the case, why bother defining a move constructor when 
> it will not get called? If I am mistaken, can you please provide a non-trivial 
> example where the move constructor gets called?

void f(S s);
void g(S a) {
     f(a);  // copy constructor called because not last use of `a`
     f(a);  // move constructor called because last use of `a`
}


>>> 3. The DIP should explicitly state what happens when you pass an rvalue 
>>> instance of an EMO by ref. How does that interact with 
>>> `-preview=rvaluerefparam` ?
>>
>> With EMOs, there is no need to use the 'ref' annotation. If you do use 'ref', 
>> the special EMO semantics do not apply.
>>
> So I assume you get an error?

No, it just works the way it does now.

> Also, what happens with `auto ref` deduction when 
> called with an EMO.

Auto ref parameters are only for template functions. "An auto ref function 
template parameter becomes a ref parameter if its corresponding argument is an 
lvalue, otherwise it becomes a value parameter"

https://dlang.org/spec/template.html#auto-ref-parameters

It will continue to do exactly what it says.


> The DIP has this example:
> 
> ref S fwd(return ref S s) { return s; }
> 
> void f(S s);
> ...
> S s;
> f(fwd(s));
> f(fwd(S());
> 
> Assuming S is an EMO, when we have `f(fwd(S()))` what happens here? Is a 
> reference to the rvalue passed to `fwd` or does the move constructor get called?
> If we simply call `f(S())`, what happens here? Is `S()` passed by move ref or do 
> we have a move constructor call?
> 
> The DIP talks about move refs, but the examples only use lvalues. Is the move 
> constructor ever called for an rvalue instance of an EMO?

What happens is just what the spec says:

"Ownership of the argument to fwd() is retained by the caller, and so the caller 
will be responsible for its destruction. When the call is made to f(), a copy is 
made."

No move copies or move assignments are done. BUT, if the compiler can look 
inside the fwd() function, it can see that `s` can be moved directly to `f` in 
the first case, and `S()` can be moved directly to `f` in the second. Thus, here 
the move constructor is used as an optimization.


More information about the Digitalmars-d mailing list