This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Aliasing isn't the only worry of lazy evaluation, right?

Tags: None
(comma "," separated)
Tal
Registered Member
Posts
30
Karma
0
In the lazy evaluation documentation, it says aliasing is something to worry about, except from operator *.

However, I think that there is another issue - temporal(rvalue) lifetime scope.

Consider this code:
Code: Select all
Matrix2d foo();

void test() {
    Matrix2d a;
    a << 1 ,2, 3, 4;
    Matrix2d result = foo() + a;// Is result value has defined behavior?
}


The reason I worry is because foo() return object is temporal value(prvalue), and it's lifetime scope ends up after lazy evaluation creation.

According to C++, const reference(i.e. const T&) to temporal(rvalue) objects extend the lifetime of the object to this const reference lifetime.
However, if I understood correctly, lifetime isn't extended even longer if this const reference is given to another const reference.

Because of that, I think that this is another issue with lazy evaluation. Am I right?


Tal
Registered Member
Posts
30
Karma
0
I found source to my claim:
http://bytes.com/topic/c/answers/806030 ... t-lifetime

Is this problem known? Haven't found it in docs


jaschau_
Registered Member
Posts
2
Karma
0
I don't think that the link you have posted is related to your code example. In that link, the problem that occurs is that a reference to a member of a temporary object is used. Note that usage of a const reference for the member doesn't help you in that case: while a const reference can extend the lifetime of a temporary variable, this const reference is not related to the original temporary object whose lifetime thus ends immediately after the line Bar().GetFoo(); Consequently, the reference to its member also becomes invalid immediately after hat line.
In your code example, const references are created that are directly linked to the temporary object returned by foo() and thus extend the object's lifetime until the entire line Matrix2d result = foo() + a; has been processed.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
As jaschau_ wrote, there is no problem with your code snippet, however there do exist other pitfalls as mentioned there:

http://eigen.tuxfamily.org/index.php?ti ... _templates

Another one based on your example would be to declare 'result' using auto:

auto result = foo() + a;

In that case the variable 'result' will reference a dead object.
Tal
Registered Member
Posts
30
Karma
0
Thanks for your response!

I run my example code and it works fine as you mentioned.

But I still don't understand why it's working.

Here is what I think that happens:
1. calculate foo()
2. create a CwiseBinaryOp with constructor (left, right, functor)
2. a. initialize the "left" constructor reference to foo() value and extend it lifetime to the constructor lifetime
b. initialize the "right" reference and "functor" (not important here)
c. call the constructor
c. 1. inside the constructor, initialize the "left" member reference with "left" parameter reference.
...
3. call "result" constructor with the CwiseBinaryOp.

Now what you said is that initialize a reference with temporary extends the temporary lifetime to this reference scope. However, the reference scope is the constructor, NOT the object.

After that this parameter reference is initialize, initializing the member reference doesn't extend(so I think) the temporary lifetime again.

Think of this logic like this:
Suppose CwiseBinaryOp constructor wouldn't be inlined in the header, and the implementation was in external cpp unit. How could the compiler know that the reference that was given in the CwiseBinaryOp constructor is used as a member reference? (in order to extend lifetime to that CwiseBinaryOp object)
Whether this constructor is inlined or not, shouldn't change the C++ rules on lifetime scopes.

So I guess you right, but I still don't know why.
Any idea?


User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
Nope, in your example, the temporary returned by foo() is pushed on the stack and its lifetime is extended to whole statement. You can check by yourself:

Code: Select all
#include <iostream>
struct Foo {
  Foo(int v) : val(v) {}
  ~Foo() { std::cout << "Bye bye Foo\n"; }
  int val;
};

struct Bar {
  const Foo &ref;
  Bar(const Foo &r) : ref(r) {}
  ~Bar() { std::cout << "Bye bye Bar\n"; }
};

Foo make_foo(int val) {
  return Foo(val);
}

void print(const Bar &b)
{
  std::cout << "b.ref.val = " << b.ref.val << "\n";
}

int main() {
  print(Bar( make_foo(7) ));
}
Tal
Registered Member
Posts
30
Karma
0
ggael wrote:Nope, in your example, the temporary returned by foo() is pushed on the stack and its lifetime is extended to whole statement. You can check by yourself:

Code: Select all
#include <iostream>
struct Foo {
  Foo(int v) : val(v) {}
  ~Foo() { std::cout << "Bye bye Foo\n"; }
  int val;
};

struct Bar {
  const Foo &ref;
  Bar(const Foo &r) : ref(r) {}
  ~Bar() { std::cout << "Bye bye Bar\n"; }
};

Foo make_foo(int val) {
  return Foo(val);
}

void print(const Bar &b)
{
  std::cout << "b.ref.val = " << b.ref.val << "\n";
}

int main() {
  print(Bar( make_foo(7) ));
}


I've looked again at this template class and on the internal:nested.
I understand that the template argument for temporary object isn't const T& but T(or maybe T&& in C++x0), and T& or const T& for lvalues.

I see now why you're genius ;)

Thank you very much!




Bookmarks



Who is online

Registered users: bartoloni, Bing [Bot], Google [Bot], Yahoo [Bot]