Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloading operator[] and NOT getting "lvalue required as left operand of assignment" error

This is a bit of an inverse of all the "lvalue required as left operand of assignment" error questions.
I have a class that overloads operator[], but only the version that returns a temporary. If it were to return an int:

struct Foo
{
    int operator[]( int idx ) const { return int( 0 ); }
};

Foo f;
f[1] = 5;

I would rightfully get the lvalue compiler error. If it returns a struct type however, the compiler ( GCC 7.2 in this case ) doesn't complain at all:

struct Bar {};
struct Foo
{
    Bar operator[]( int idx ) const { return Bar(); }
};

Foo f;
f[1] = Bar();

Why wouldnt this complain in the same way if Bar is a temporary and it doesnt have an specialized operator =? Alternate question, is there some way to make this complain? Clearly this is a coding error if it were to be used this way

like image 983
ByteMe95 Avatar asked Jun 17 '19 22:06

ByteMe95


1 Answers

is there some way to make this complain?

You can use an explicitly defaulted assignment operator with a ref-qualifier:

struct Bar {
    Bar& operator=(const Bar&) & = default;
//                             ^

This makes assignment of an rvalue ill-formed, while assignment of an lvalue remains well-formed.

Note that declaring the assignment operator disables implicit move assignment so you may need to define that as well, if needed (also as defaulted, and possibly with an rvalue ref qualifier, if appropriate).

Why wouldnt this complain in the same way if Bar is a temporary and it doesnt have an specialized operator =?

Because the implicitly generated assignment operators are not ref-qualified.

Clearly this is a coding error if it were to be used this way

Assignment of an rvalue is not universally an error. For some types that are supposed to behave like references, assignment of an rvalue is natural. That is so because the assignment modifies the referred object, and not the temporary object itself.

A typical use case is assigning to an rvalue std::tie (example from cppreference):

std::set<S> set_of_s; // S is LessThanComparable

S value{42, "Test", 3.14};
std::set<S>::iterator iter;
bool inserted;

// unpacks the return value of insert into iter and inserted
std::tie(iter, inserted) = set_of_s.insert(value);

Yes, it might be better if the implicit operators were qualified, and explicit declaration was required for non-qualified, considering referential types are exceptional rather than the norm. But that's not how the language is and changing it is a backwards incompatible change.

like image 54
eerorika Avatar answered Sep 25 '22 07:09

eerorika