Stupid scope destruction question

monarch_dodra monarchdodra at gmail.com
Thu Sep 20 04:35:56 PDT 2012


On Thursday, 20 September 2012 at 09:31:45 UTC, Denis 
Shelomovskij wrote:
> 20.09.2012 13:27, Denis Shelomovskij пишет:
>> Is there any guaranties that `ScopeTemp` will not be destroyed 
>> before
>> `f` call because it isn't used?
>> ---
>> struct ScopeTemp
>> {
>>     ...
>>     // allocates value
>>     this(...) { ... }
>>
>>     @property void* value() { ... }
>>
>>     // destroys value
>>     ~this() { ... }
>> }
>>
>> void f(void* ptr) { ... }
>>
>> void main()
>> {
>>     f(ScopeTemp(...).value);
>> }
>> ---
>> According to http://dlang.org/struct.html#StructDestructor
>> "Destructors are called when an object goes out of scope."
>> So I understand it as "it will not be destroyed before scope 
>> exit even
>> if not used". Is it correct?
>>
>> So the question is if `ScopeTemp`'s scope is `main` scope, not 
>> some
>> possibly generated "temp scope" (don't now what documentation 
>> statements
>> prohibit compiler from doing so).
>>
>>
>> Working code:
>> ---
>> import std.exception;
>> import std.c.stdlib;
>>
>> struct ScopeTemp
>> {
>>     private int* p;
>>     // allocates value
>>     this(int i)
>>     in { assert(i); }
>>     body { *(p = cast(int*) enforce(malloc(int.sizeof))) = i; }
>>
>>     @disable this(this);
>>
>>     @property int* value() { return p; }
>>
>>     // destroys value
>>     ~this() { *p = 0; p = null; /*free(p);*/ }
>> }
>>
>> void f(int* ptr)
>> {
>>     assert(*ptr == 1);
>> }
>>
>> void main()
>> {
>>     f(ScopeTemp(1).value);
>> }
>> ---
>
> Wow, this `main` works fine too:
> ---
> // same ScopeTemp definition as above
>
> int* gptr;
>
> void f(int* ptr)
> {
>     assert(*ptr == 1);
>     gptr = ptr;
> }
>
> void g()
> {
>     assert(*gptr == 0);
> }
>
> void main()
> {
>     f(ScopeTemp(1).value);
>     // Here `ScopeTemp` value is already destroyed
>     g();
> }
> ---
>
> So `ScopeTemp`'s scope definitely isn't a `main` scope. So the 
> question: what do we know about this "temp scope"?

AFAIK, if the rules are the same in C++ (which they probably 
are), then:
"Any object constructed during argument passing will remain valid 
for the duration of the call. It will go out of scope once the 
function has finished returning, and after the return value has 
itself gone out of scope and been destroyed."

The two catches are:
*"The implementation is allowed to elide the copy for pass by 
value, even if it has visible side effects, and construct 
directly into the function's stack."
*"If the function is able to use "(Named) Return Value 
Optimization" "(N)RVA" (invented by Walter himself), the returned 
value's destruction will not be observable (as it won't happen)."

Try running this:

C++
----
#include <iostream>

struct S
{
   int i;
   S(int i) : i(i) {::std::cout << "C:" << i << ::std::endl;}
   S(const S&){::std::cout << "CC:" << i << ::std::endl;}
   ~S(){::std::cout << "D~:" << i << ::std::endl;}
};

S foo(S)
{
   ::std::cout << "foo(S)" << ::std::endl;
   return S(2);
}
S foo(int)
{
   ::std::cout << "foo(int)" << ::std::endl;
   return S(6);
}

int main()
{
   foo(S(1));
   foo(S(5).i);
}
----
C:1
foo(S)
C:2
D~:2
D~:1
C:5
foo(int)
C:6
D~:6
D~:5
----

D:
----
import std.stdio;

struct S
{
   int i;
   this(int j){i = j;writeln("C",i);}
   this(this){writeln("PB",i);}
   ~this(){writeln("D~",i);}
};

S foo(S)
{
   writeln("foo(S)");
   return S(2);
}
S foo(int)
{
   writeln("foo(int)");
   return S(6);
}

void main()
{
   S a = foo(S(1));
   S a = foo(S(5).i);
}
----
C1
foo(S)
C2
D~1
D~2
C5
foo(int)
C6
D~6
D~5
----

I haven't seen any difference between C++ and D, including the 
argument pass by value elision, as well as (N)RVA


More information about the Digitalmars-d-learn mailing list