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