1

Microsoft lvalue definition:

An lvalue refers to an object that persists beyond a single expression.

A second definition:

The expression E belongs to the lvalue category if and only if E refers to an entity that ALREADY has had an identity (address, name or alias) that makes it accessible outside of E.

I wrote the following code:

class A{};

const A& f1()
{
    return A();
}

const int& f2()
{
    return 1;
}

int main() {
    cout<<&f1()<<endl; // this prints everytime "0".
    cout<<&f2()<<endl; // this prints everytime "0".
    return 0;
}
  1. Why f1() and f2() are lvalue expressions?
  2. Why an address of lvalue reference to rvalue is zero?
  3. Why are both definitions equivalent?
Ron
  • 13,116
  • 3
  • 25
  • 45
Stav Alfi
  • 10,681
  • 15
  • 74
  • 124
  • 4
    A decent compiler would say `warning: returning reference to local temporary object [-Wreturn-stack-address]`. You have undefined behaviour. – juanchopanza Dec 23 '17 at 23:11
  • I can only print an address of `lvalue` expression. If I succeed, I guess it's `lvalue`(?). – Stav Alfi Dec 23 '17 at 23:13
  • 3
    What makes you think `f1()` returns a reference to an lvalue compliant with either of those definitions? Because it compiles? That isn't the only litmus test for correct, standard-compliant code. You still have to do your part. Turn up your warnings and treat them as errors. – WhozCraig Dec 23 '17 at 23:17
  • 3
    The linked Microsoft page is severely wrong, please disregard. The C++ Standard is a better place to get information than random websites. The second definition you use also has problems (as evinced by your code) – M.M Dec 23 '17 at 23:45

3 Answers3

3
  1. Why f1() and f2() are lvalue expressions?

Because each are a function call to a function that returns an lvalue reference.

Standard draft: [expr.call]

11 A function call is an lvalue if the result type is an lvalue reference type or ...


Why the & character after the type name makes it an lvalue reference?

Standard draft: [dcl.ref]

1 In a declaration T D where D has either of the forms

& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list reference to T” ...

2 A reference type that is declared using & is called an lvalue reference ...


  1. Why an addres of lvalue reference to rvalue is zero?

The behaviour is undefined.

Standard draft: [expr.unary.op]

3 The result of the unary & operator is ... the result has type “pointer to T” and is a prvalue that is the address of the designated object

There is no designated object, and the standard doesn't define the behaviour of the addressof operator in that case.

Standard draft: [defns.undefined]

behavior for which this document imposes no requirements

[ Note: Undefined behavior may be expected when this document omits any explicit definition of behavior ...


  1. Why are both definitions equivalent?

They aren't necessarily equivalent. One or both of them may be incorrect. Both appear to be descriptions of lvalue expressions, rather than definitions.

The normative definition is in the C++ standard document.

What is the definition of lvalue by the standard?

Standard draft: [basic.lval]

  • (1.1) A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.

...

  • (1.3) An xvalue is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime). [ Example: Certain kinds of expressions involving rvalue references ([dcl.ref]) yield xvalues, such as a call to a function whose return type is an rvalue reference or a cast to an rvalue reference type. — end example  ]

  • (1.4) An lvalue is a glvalue that is not an xvalue.

The [expr] section defines each possible expression in the language, and if the expression is an lvalue, then that is stated. "is an lvalue" occurs 37 times, but this simple search is not necessarily exhaustive.

eerorika
  • 181,943
  • 10
  • 144
  • 256
1

You end up returning a hanging reference because the returned reference has nothing to reference because A() is destructed at the end of the method. What you have is undefined behavior. The 0 is a placeholder for the fact that it is not referencing any memory location. The f2() function returns another temporary variable as reference. To be absolutely clear the memory location they return is 0 because the memory location they reference does not exit any longer.

Hope this helps.

Jake Freeman
  • 1,706
  • 1
  • 6
  • 15
1

Declaring a function with lvalue reference return type means that that function call is an lvalue expression (nothing more and nothing less).

The pages you linked to are both wrong in equating lvalue expressions with objects that "already exist" or "persist" or whatever. In your code is an example of an lvalue expression that refers to an object that only existed during the function call.

Using the result of the function call causes undefined behaviour because the behaviour of lvalue expressions is only defined for when they actually refer to an object. (Plus a few cases for referring to a potential object under construction or destruction, but that doesn't apply here, since the object's associated storage is already released by the time the calling code uses the result of the expression).

Undefined behaviour means anything can happen, including (but not limited to) outputting a zero.

M.M
  • 130,300
  • 18
  • 171
  • 314