views:

314

answers:

5

A function call returning a structure is an rvalue expression, but what about its members?
This piece of code works well with my g++ compiler, but gcc gives a error saying "lvalue required as left operand of assignment":

struct A
{
    int v;
};

struct A fun()
{
    struct A tmp;
    return tmp;
}

int main()
{
    fun().v = 1;
}

gcc treats fun().v as rvalue, and I can understand that.
But g++ doesn't think the assignment expression is wrong. Does that mean fun1().v is lvalue in C++?
Now the problem is, I searched the C++98/03 standard, finding nothing telling about whether fun().v is lvalue or rvalue.
So, what is it?

+1  A: 

Edit: Ok, I guess I finally have something from the standard:

Note that v is of type int which has an built-in assignment operator:

13.3.1.2 Operators in expressions

4 For the built-in assignment operators, conversions of the left operand are restricted as follows: — no temporaries are introduced to hold the left operand, and [...]

fun1() should return a reference. A non-reference/pointer return type of a function is a r-value.

3.10 Lvalues and rvalues

5 The result of calling a function that does not return an lvalue reference is an rvalue [...]

Thusly, fun1().v is a rvalue.

8.3.2 References

2 A reference type that is declared using & is called an lvalue reference, and a reference type that is declared using && is called an rvalue reference. Lvalue references and rvalue references are distinct types.

dirkgently
Huh? He should return a reference to a temporary?
GMan
If it's an lvalue, why can you assign to it? And rvalue references are not yet part of official standard C++.
Omnifarious
There's no doubt that `fun1()` is an rvalue, per 3.10, but please read the question. The expression `fun1().v` is **not** the result of calling a function. It is parsed as `(fun1()) . x` - member access.
MSalters
@MSalters: Edited post with what I think helps.
dirkgently
@MSalters: Is there a reason to believe that given an rvalue object of aggregate type, it's members can be lvalues?
dirkgently
The member access expression `(ObjectExpr).member` obviously is either an rvalue or an lvalue, and intuitively it makes sense that it would be an rvalue iff `(ObjectExpr)` is an rvalue. But where's that rule in the standard? Intuition is not good enough. Note that I'n agreeing with you that `fun1()` is an rvalue expression.
MSalters
I have added a point in the standard where it states that the result of data member pointer dereferencing will only be a lvalue if the object is an lvalue. This is not exactly the same, but the behavior should be the same.
David Rodríguez - dribeas
dirkgently
5.3.5 [expr.ref], see post below.
MSalters
A: 

You code has no scene. Returned structure is allocated on stack, so assignment result is immediately will be lost.

Your function should eiter allocate new instance of A by:

new A()

In this case better signature

A* f(){ ...

Or return existing instance, for example:

static A globalInstance;
A& f(){ 
  return globalInstance;
}
Dewfy
"has no scene"? What does that mean?
Andrew Medico
It also has no street cred!
FredOverflow
Your comment does not make sense.
phresnel
@Andrew Medico - when function returns stack allocated value then value should be stored somewhere. Otherwise your assignment would be lost. That is why g++ treats this case as sense-less. In my post I've explained why.
Dewfy
@Dewfy: g++ does not treat this as "sense-less", see my above post about why this behaviour is allowed in C++. Also, we did not know what you mean by "scene" (vs. sense). Further, just because the result of something is unused would not justify validness. Furthermore, in C++, you can very well re-use a statement like _Foo().x=5_ , like in e.g. _(Foo().x=5).member()_
phresnel
The code is obviously simplified, but the fundamental question is still there. If the member would be `volatile`, the assignment is observable and g++ _must_ do the assignment - except for the whole rvalue/lvalue discussion.
MSalters
@phresnel (Foo().x=5).member() supposes only overridden 'operator =' which returns not int, but reference to 'struct A' in this case g++ wouldn't produce warning.
Dewfy
You'll laugh, but "(Foo().x = 5).member()" is perfectly valid code, for which g++ will not and should not emit a diagnostic in "struct Foo { Foo } Foo Foo (); static Foo foo; void member() {}};Foo Foo::foo;Foo::Foo() : x(foo) {}int main () { (Foo().x = 5).member();}"
phresnel
@Dewfy: sorry for the formatting, you'll see better if you paste the code within "".
phresnel
Dewfy
@phresnel: By the way, use ticks: \`code goes here\` to produce `code goes here`. I used backslash to escape the former ticks
GMan
`cool`, thanks :)
phresnel
A: 

I've noticed that gcc tends to have very few compunctions about using rvalues as lvalues in assignment expressions. This, for example, compiles just fine:

class A {
};

extern A f();

void g()
{
   A myA;
   f() = myA;
}

Why that's legal and this isn't (i.e. it doesn't compile) though really confuses me:

extern int f();

void g()
{
   f() = 5;
}

IMHO, the standard committee has some explaining to do with regards to lvalues, rvalues and where they can be used. It's one of the reasons I'm so interested in this question about rvalues.

Omnifarious
This is really interesting... I would say that the compiler is wrong, but I understand how they got there. The second block is explicitly prohibited by the standard: you cannot modify an rvalue, and in 5.17/1 assignment operator requires an lvalue as first argument. Now the standard does allow you to call non-constant methods on rvalues, and I can only assume that after generating the assignment operator, the compiler is translating the first block into: `f().operator=(myA)` and considering the `operator=` as a regular method, regardless of _All [=] require a modifiable lvalue_ in 5.17/1
David Rodríguez - dribeas
@David: Do you think that if someone explicitly did `f().operator=(myA)` that it should be considered legal?
Omnifarious
No, but I can see someone assuming that since `operator=` is a member function (even if it performs modifications) it should be allowed to call it on an lvalue. `class X { void m(); }; X f(); void h() { f().m() }` is a valid piece of code. The problem to me is that even if that is true, allowing `operator=` to be called on an lvalue would prove inconsistent with primitive types (where the standard is clear), so if I was to make the call, I would not allow it. After all, even if `operator=` is a member function, it is _special_
David Rodríguez - dribeas
@Omnifarious - I think your code shows why it's in general advisable to return "const A", as that way there is no discrepancy between primitive types and user-defined types. This is usually recommended in the context of operator overloading (e.g., so that operator+(T, T) returns const T) but I think it applies to any function returning a user-defined type.
Manuel
@Manuael: I used to agree with you but with rvalue references in C++0x, where does that leave things? I want return values to be able to be used as non-const rvalue references.
Omnifarious
Actually I don't see any problem with this example. For class type, the assignment is performed by the member operator = . If there's no user-defined operator = , a default one is generated. Since a member function call doesn't need a lvalue, it's always OK to do assignment on class type unless explicitly forbidden with a private operator = . And int is a built-in type, so there is no member operator.
hpsMouse
A: 

It becomes obvious when you consider that the compiler will generate a default constructor, a default copy constructor, and a default copy assignment operator for you, in case your struct/class does not contain reference members. Then, think of that the standard allows you to call member methods on temporaries, that is, you can call non-const members on non-const temporaries.

See this example:

struct Foo {};
Foo foo () {
    return Foo();
}

struct Bar {
private:
    Bar& operator = (Bar const &); // forbid
};
Bar bar () {
    return Bar();
}
int main () {
    foo() = Foo(); // okay, called operator=() on non-const temporarie
    bar() = Bar(); // error, Bar::operator= is private
}

If you write

struct Foo {};
const Foo foo () { // return a const value
    return Foo();
}

int main () {
    foo() = Foo(); // error
}

i.e. if you let function foo() return a const temporary, then a compile error occurs.

To make the example complete, here is how to call a member of a const temporarie:

struct Foo {
    int bar () const { return 0xFEED; }
    int frob ()      { return 0xFEED; }
};
const Foo foo () {
    return Foo();
}

int main () {
    foo().bar(); // okay, called const member method
    foo().frob(); // error, called non-const member of const temporary
}

You could define the lifetime of a temporary to be within the current expression. And then that's why you can also modify member variables; if you couldn't, than the possibility of being able to call non-const member methods would be led ad absurdum.

edit: And here are the required citations:

12.2 Temporary objects:

  • 3) [...] Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. [...]

and then (or better, before)

3.10 Lvalues and rvalues:

  • 10) 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. ]

And an example use: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter

phresnel
I think you might be having the same confusion I had. See my comment to this answer: http://stackoverflow.com/questions/2220230/is-a-member-of-an-rvalue-structure-an-rvalue-or-lvalue/2220281#2220281
Manuel
@Manuel: Tbh, I am confused about why you think that I am confused!?
phresnel
@phreshel - Read the comment I linked to and the response by dribeas. Your reasoning is correct but does not apply here because v is of a primitive type.
Manuel
When I think about this it becomes interesting.A member function call to an rvalue object is allowed, and to call a member function, the object must be binded to "this" pointer, which changes the object into an lvalue during the runtime of the function call. That means an rvalue object can sometimes become an lvalue.But member variable accessing is a little different, and is not mentioned by the standard.And I hate that "under certain circumstances", by the way. :-)
hpsMouse
+6  A: 

A member of an rvalue expression is an rvalue.

The standard states in 5.3.5 [expr.ref]:

If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue [...] - If E2 is a non-static data member, and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue.

David Rodríguez - dribeas
+1 This is the one precise answer. Expression E1 is `fun()` in the original example, E2 is `v`, without cv qualification. `v` is also not static and not a reference.
MSalters
I would not have found it if it were not because of you insisting on greater precision on the first answer (that I am deleting now).
David Rodríguez - dribeas
The section is 5.2.5. To be anal, that paragraph doesn't specify anything in case E1 is not an lvalue. C++0x, however, appends "otherwise, it is an rvalue".
Potatoswatter
I have read this part of the standard, too. It doesn't tell anything about what happens when E1 is not an lvalue. That's why I'm so confused...And if C++0x says it's an rvalue, it answers the question. Maybe it's just a little bug for the current standard? :)
hpsMouse
It has been amended in the upcoming standard as of DR421, so that there is an extra 'otherwise, it is an rvalue', as Potatoswatter points out. According to the standard committee, while the wording is unfortunate the intent was there: http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#421
David Rodríguez - dribeas
Thanks, David. Your information really helps.
hpsMouse