c++copy-constructormethod-chainingreturn-by-referencecopy-initialization

Use of the chain method in the continuation of the copy constructor/initialization in one declaration statements in c++?


As you know, we usually use the return by reference for the method chaining, I use the return by reference in the first code and the output is as I have predicted. In the second code block, when I did not use the return by reference, the chain was broken and my expected output was not generated, but in the third code block, I have reached the desired result using the chain method in continuation of the copy constructor / initialization in one declaration statements (without using return by reference), the question is, what is the logic behind the third code that protects the chain from breaking?

class Calc
{
private:
    int m_value;

public:
    Calc(int value = 0) : m_value{ value } {}

    Calc& add(int value) { m_value += value; return *this; }
    Calc& sub(int value) { m_value -= value; return *this; }
    Calc& mult(int value) { m_value *= value; return *this; }

    int getValue() { return m_value; }
};

int main()
{
    Calc calc;
    calc.add(5).sub(3).mult(4); // its OK and output 8

    std::cout << calc.getValue() << '\n';
    return 0;
}

class Calc
{
private:
    int m_value;

public:
    Calc(int value = 0) : m_value{ value } {}

    Calc add(int value) { m_value += value; return *this; }
    Calc sub(int value) { m_value -= value; return *this; }
    Calc mult(int value) { m_value *= value; return *this; }

    int getValue() { return m_value; }
};

int main()
{
    Calc calc;
    calc.add(5).sub(3).mult(4); // its NOT OK and output 5

    std::cout << calc.getValue() << '\n';
    return 0;
}

class Calc
{
private:
    int m_value;

public:
    Calc(int value = 0) : m_value{ value } {}

    Calc add(int value) { m_value += value; return *this; }
    Calc sub(int value) { m_value -= value; return *this; }
    Calc mult(int value) { m_value *= value; return *this; }

    int getValue() { return m_value; }
};

int main()
{
    Calc calc = Calc{0} // copy constructor / initialization
        .add(5) // method chaining
        .sub(3) // method chaining
        .mult(4); // method chaining, its OK and output 8

    std::cout << calc.getValue() << '\n';
    return 0;
}

Solution

  • what is the logic behind the third code that protects the chain from breaking?

    Chain does get break, but you use the final result to assign to calc

    Calc calc = Calc{0}.add(5).sub(3).mult(4);
    

    is equivalent to

    Calc calc = Calc{2}.mult(4);
    

    What ultimately gets copy constructed into calc is a temporary Calc object when multiplied by 4.