In the following snippet, why does the line o.margin() = m; compile without fault? It easily deserves a warning, since it will almost invariably be a mistake. I would actually have thought it to be an error since it puts an R-Value on the left side of an assignment.
#include <iostream>
struct Margin
{
    Margin(int val=0) : val(val) {};
    int val;
};
struct Option
{
    Margin m;
    int z=0;
    Margin margin()const { return m; }
    int zoomLevel() { return z; }
};
int main()
{
    Option o;
    std::cout << "Margin is: "<< o.margin().val << std::endl;
    Margin m = { 3 };
    // The following line is a no-op, which generates no warning:
    o.margin() = m;
    // The following line is an error
    // GCC 4.9.0: error: lvalue required as left operand of assignment
    // clang 3.8: error: expression is not assignable
    // MSVC 2015: error C2106: '=': left operand must be l-value
     o.zoomLevel() = 2;
    std::cout << "Margin is: "<< o.margin().val << std::endl;
    return 0;
}
Output:
Margin is: 0
Margin is: 0
                You are allowed to modify return types of class type (by calling non const methods on it):
3.10/5 from n4140
5 An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [ Example: a member function called for an object (9.3) can modify the object. —end example ]
your code:
o.margin() = m;
is actually the same as
o.margin().operator=( Margin(m) );
so non const method is called, if you change it to:
o.margin().val = m;
then you will get an error.
on the other hand here:
o.zoomLevel() = 2;
zoomLevel() returns non-class type, so you cannot modify it.
When o is an object of class type, operator= is a member function. The code    o.margin() = m; is equivalent to o.margin().operator=(m);.
You are allowed to call members functions of temporary class objects, similar to how you access a member in o.margin().val.
Also, the class' assignment operator can be overridden and not be a no-op at all.
If you want to forbid such uses, since C++11 you can use a reference qualifier on the assignment operator:
Margin& operator=(const Margin&) & = default;
This will generate the following error on GCC 5.1:
error: passing 'Margin' as 'this' argument discards qualifiers [-fpermissive]
You might also want to check this related question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With