13

I'm wondering why returning const reference of a local object is illegal whereas returning a local object is legal as long as you assign it to a const reference?

vector<int> f_legal() {
    vector<int> tempVec;
    tempVec.push_back(1);
    return tempVec;
}

const vector<int>& f_illegal() {
    vector<int> tempVec;
    tempVec.push_back(1);
    return tempVec;
}

void g() {
    const vector<int>& v1 = f_legal(); // legal
    const vector<int>& v2 = f_illegal(); // illegal
}

Edit: My point is that if assigning a const ref to a returned local variable is legal, then shouldn't assigning a const ref to a returned const ref of a local variable be legal as well?

Monster Hunter
  • 846
  • 1
  • 12
  • 24
  • 3
    @Ed Heal Yes it's well-defined in C++ that a local const reference will extend the lifetime of a temporary to which it is bound. – Mark B Feb 12 '16 at 16:57
  • 3
    @EdHeal yes, it's legal. There's a special case in the standard that will keep a local object alive as long as there's a const reference to it in the current scope. – Mark Ransom Feb 12 '16 at 16:57

3 Answers3

15

Returning a reference to a local variable is illegal (Undefined Behavior). Period. There is no const or mutable clause.

This is because local function variables have automatic storage duration. They are "destroyed" after the function exits. If the function returns a reference to such a variable, that reference it is said to be dangling: it refers to an object that no longer exists.

The first one is legal because of a special C++ rule: initializing a reference to a prvalue extends the lifetime of that temporary object to the lifetime of the reference.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • The rule actually has nothing to do with `const` -- any reference which is *directly* bound to a temporary extends the lifetime of that temporary. In C++98, other rules provided that only const references could directly bind to a temporary, those rules have now changed, which had a domino effect on the lifetime extension rule. – Ben Voigt Feb 12 '16 at 17:02
  • @BenVoigt That's the first I hear of this. Could you provide a reference? – bolov Feb 12 '16 at 17:03
  • @BenVoigt Thank you. It was most helpful. – bolov Feb 12 '16 at 17:08
  • @BenVoigt Yet `int& r = 3;` does not compile in C++ 17, while `const int& r = 3;` does. Why? – Géry Ogam Apr 20 '20 at 11:56
  • @Maggyero please see this: https://stackoverflow.com/a/52104874/2805305 – bolov Apr 20 '20 at 13:52
  • BenVoigt said "The rule actually has nothing to do with `const`" But in my example `const` has an influence, so this sentence is incorrect. If "any reference which is *directly* bound to a temporary extends the lifetime of that temporary.", how do you explain that `int& r = 3;` does not compile? – Géry Ogam Apr 20 '20 at 14:33
  • 1
    @Maggyero: That is not a reference directly bound to a temporary, it is a compile error. But `int&& r = 3;` does compile, and does extend the lifetime of the temporary. – Ben Voigt Apr 20 '20 at 15:20
  • @BenVoigt Okay I see your point. But this compile error occurs when using a non-`const` lvalue reference, so saying that "The rule actually has nothing to do with `const`" might be a bit misleading. – Géry Ogam Apr 20 '20 at 17:45
  • @Maggyero it's a different error. You can't bind a non-const reference to a temporary. We are talking about extending the life of a temporary with a reference. Any reference that you bind to a temporary extends the lifetime of that temporary. – bolov Apr 20 '20 at 18:40
  • "You can't bind a non-const reference to a temporary" In this case I would rather say "Any reference that you **can** bind to a temporary extends the lifetime of that temporary." Otherwise the reader might think that all references can be bind. – Géry Ogam Apr 20 '20 at 19:31
  • 2
    @Maggyero: You also can get reference binding compile errors from type mismatches, or other qualifier mismatches like trying to remove volatile without `const_cast`. If I say that a reference *is* directly bound, then naturally it must follow that I am only talking about scenarios where it is possible. – Ben Voigt Apr 20 '20 at 19:35
  • and btw the comment you have a problem with says "any reference which is directly bound to a temporary extends the lifetime of that temporary" which is more very clear. – bolov Apr 20 '20 at 19:48
  • @BenVoigt Seing "nothing to do with `const`" and "any reference" in the same sentence can lead to the interpretation that any type of reference, `const` or not, can bind. At least that is how I interpreted it. That was not even clear for @bolov himself: "That's the first I hear of this. Could you provide a reference?" But now that you have clarified there is no more ambiguity. – Géry Ogam Apr 20 '20 at 21:41
14

Even if you assign it to a const reference, the return value is declared as passed by value, that means it'll be copied[1] to outside as a temporary object, and then binded to the const reference. Binding temporary object to a const reference is fine, the object won't be destroyed until getting out of the lifetime of the const reference.

On the other hand, returning reference of a local variable is illegel. The local variable'll be destroyed when the function returned, that means the outside reference will be dangled.

EDIT

My point is that if assigning a const ref to a returned local variable is legal, then shouldn't assigning a const ref to a returned const ref of a local variable be legal as well?

The point is the 1st case is not assigning a const ref to a returned local variable, it's assigning a const ref to a returned temporary variable. (Which might copied from the local variable.)


[1] The copy might be omitted according to RVO technically.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

Most likely because it would totally ruin the whole stack based calling conventions that have served us well for decades...that pretty much every CPU assumes.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125