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

Reply via email to