Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why chaining requires operator to return reference?

The most popular operator which should return reference is operator=

class Alpha
{
    int x;
    int y;
    std::string z;
  public:
    void print()
        { cout << "x " << x << " y " << y << "  " << z << '\n'; }
    Alpha(int xx=0, int yy=0, std::string zz=""): x(xx), y(yy), z(zz) {}
    Alpha operator=(Alpha& another)
    {
        x = another.x;
        y = another.y;
        z = another.z;
        return *this;
    }


};

int main()
{
    Alpha a(3,4,"abc"),b,c;
    b=c=a;
    return 0;
}

Clang says this:

clang++-3.6 new.cxx -o new new.cxx:70:3: error: no viable overloaded '=' b=c=a; ~^~~~ new.cxx:34:8: note: candidate function not viable: expects an l-value for 1st argument Alpha operator=(Alpha& another) ^ 1 error generated.

gcc this:

new.cxx:34:8: note: no known conversion for argument 1 from ‘Alpha’ to ‘Alpha&’

But I can't understand what's the problem in theory. What I think happens:

  1. Firstly operator= is called for object c. It receives object a by reference, copies it's values to c and returns anonymous copy of it's self (of object c): Copy soncstructor is called.
  2. Then operator= is called for object b. It requires rvalue ref, but we have only written lvalue reference, so the error occurs.

I have added rval operator= and copy constructor, which receives lvalue reference and everything works, now I have no idea why (I should have written rvalue copy constructor which receive const Alpha& s or Alpha&& s):

class Alpha
{
    int x;
    int y;
    std::string z;
  public:
    void print()
    { cout << "x " << x << " y " << y << "  " << z << '\n'; }
    Alpha(int xx=0, int yy=0, std::string zz=""): x(xx), y(yy), z(zz) {}
    //Alpha(Alpha&& s): x(s.x), y(s.y), z(s.z) {}
    Alpha(Alpha&& ) = delete;
    Alpha(Alpha& s): x(s.x), y(s.y), z(s.z) {}
    Alpha operator=(Alpha& another)
    {
        x = another.x;
        y = another.y;
        z = another.z;
        return *this;
    }
    Alpha operator=(Alpha&& another)
    {
        x = another.x;
        y = another.y;
        z = another.z;
        return *this;
    }

};
like image 937
yanpas Avatar asked Dec 24 '22 09:12

yanpas


1 Answers

This signature for an assignment operator

Alpha operator=(Alpha& another)

is unusual in two ways. The first is that it returns a copy of the assigned-to object. Very rare to do that. The other is that it accepts a non-const reference as a parameter.

The non-const reference makes it not accept temporary objects as a parameter (as those will only bind to a const lvalue reference).

In combination this means that the temporary returned from the first operator= can not be used as a parameter to the second operator=.

Your options are to either return a reference, or to make the parameter Alpha const&. Both options would work individually, or in combination.

The third option, as you found out, is to explicitly add a move assignment operator, using Alpha&& that specifically accepts temporaries.

The standard way though is to declare the copy assignment operator

Alpha& operator=(Alpha const& other);

unless you have very specific reasons for selecting another signature.

like image 119
Bo Persson Avatar answered Dec 28 '22 10:12

Bo Persson