Home > Software engineering >  Is it possible to extend the life of an rvalue passed into a function?
Is it possible to extend the life of an rvalue passed into a function?

Time:09-27

I am trying to make a system for lazy evaluation, however it seems my system wont work with rvalues/temporary variable. for example

class Number;

class LazyAddition
{
public:
    Number& lhs;
    Number& rhs;
    LazyAddition(Number& lhs, Number& rhs)
        : lhs(lhs), rhs(rhs)
    {

    }

    LazyAddition(Number&& lhs, Number&& rhs)
        : lhs(lhs), rhs(rhs)
    {

    }


};

class Number
{
public:
    Number(int x)
        : x(x)
    {

    }


    LazyAddition operator (Number& rhs)
    {
        return LazyAddition(*this, rhs);
    }


    Number(LazyAddition lazy)
    {
        x = lazy.lhs.x   lazy.rhs.x;
    }

private:
    int x;
};

It works fine when passing in lvalues like

Number num1(3)
Number num2(4)
LazyAddition { num1, num2 }

it also function when passing in rvalues

LazyAddition { Number(3), Number(4) }

however after the constructor is called, the number rvalues are immediately destroyed. this is problematic since I have references to them. I thought assigning the rvalue reference to the lvalue reference in the LazyAddition constructor might expand the lifetime, but it doesn't. is there any way to achieve this, or a better way to not copy rvalues?

CodePudding user response:

Because the rvalues are bound to the parameters of the constructor firstly, and bounding them to the class members later does not further extend their lifetime.

You can define your LazyAddition class as an aggregate to avoid binding the rvalues to parameters of constructor:

struct LazyAddition
{
    const Number& lhs;
    const Number& rhs;
};

But be careful. After C 20, you should use list-initialization syntax to initialize such a LazyAddition aggregate, like LazyAddition { Number(3), Number(4) }, otherwise the lifetime will still not be extended.

CodePudding user response:

Reference members are problematic for several reasons. You need to store the Number somewhere, hence passing r-value reference cannot work your way.

I propose a completely different approach:

#include <functional>
#include <iostream>

class Number;
class LazyAddition
{
public:
    using Getter = std::function<Number(void)>;
    LazyAddition(Getter lhs,Getter rhs) : lhs(lhs), rhs(rhs) {}
    Getter lhs;
    Getter rhs;
};

class Number
{
public:
    Number(int x) : x(x) {}

    LazyAddition operator (Number& rhs) {
        return LazyAddition([t=*this](){ return t; },[rhs](){return rhs;});
    }

    Number(LazyAddition lazy)
    {
        x = lazy.lhs().x   lazy.rhs().x;
    }
    int get() { return x;}
private:
    int x;
};

int main() {
    Number num1(3);
    Number num2(4);
    auto l = num1 num2;
    std::cout << Number(l).get();
}

This is actually more flexible, because you can create a LazyAddition even before you have a Number. All you need is a callable that will return the Number only when needed.

  •  Tags:  
  • c
  • Related