C++ / a working model

35 / 163   ·   C++11   ·   9 min

References and temporary object lifetime

Keep this sentence

A reference provides an alias for an existing object. It is not an ordinary pointer that can be rebound, and it does not automatically own its target. A const reference can extend a temporary object's lifetime in specific initialization situations, but that extension does not travel arbitrarily along a parameter, a return value, or another reference.

In this lesson
  1. After binding, assignment targets the object
  2. `const` restricts the access path
  3. Temporary lifetime extension has sharp bounds
  4. Example
  5. Exercise

After binding, assignment targets the object

int& r = a binds r to a. Later, r = b assigns the value of b to a; it does not rebind r to b. The reference itself is not a separate referenced object. Taking the address of a reference yields the address of the target, and sizeof on a reference expression observes the target type rather than some hidden pointer size.

An ordinary reference definition must be initialized, and there is no legitimate null-reference state. An interface that uses T& typically expresses a required borrow of an object; T* expresses that absence is allowed. An implementation may pass a reference by address, but the standard does not require a reference to occupy any particular number of bytes. An ABI's representation cannot replace language semantics.

`const` restricts the access path

const T& prevents ordinary modification through this reference. It does not promise that the object has no other writable aliases. A non-const lvalue reference usually binds to a modifiable lvalue. A const lvalue reference can also bind many temporary values, so it is suitable for read-only parameters that should not copy large objects.

Binding may also create a converted temporary. Accepting an integer as const double&, for example, is not merely giving the original integer another name. When you need to avoid copies and conversions, check the actual types. Do not assume zero cost or complete identity just because you see a reference.

Temporary lifetime extension has sharp bounds

A direct local binding such as const std::string& text = std::string(...) can extend the temporary string's lifetime to the lifetime of the reference. The example accesses text in the same block while the target is still alive. Survival of the reference itself does not mean that every referred-to object survives; the two lifetimes can differ.

A temporary bound to a function's reference parameter usually lives until the end of the full expression that contains the call. Returning that reference from the function does not extend the temporary again. Saving the returned reference for use in the next statement is therefore risky. When returning a new result, prefer returning a string or container by value, and let return-value optimization and moves handle efficiency.

Pitfalls

  • Returning a reference to a local variable does not take that local out of the function; continued access after return is a dangling use.
  • std::string_view is not a reference lifetime-extension mechanism; observing a temporary string with it can still dangle after the statement ends.

Run an example

Minimum C++11 · complete program · Download .cpp

#include <cassert>
#include <string>

int main() {
    int a = 3;
    int b = 8;
    int& alias = a;
    alias = b;
    assert(a == 8);
    assert(&alias == &a);
    b = 10;
    assert(alias == 8);
    const std::string& text = std::string(3, 'x');
    assert(text == "xxx");
}

Compile locally

g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread memory-references.cpp -o example && ./example

Expected result

Expected: exit 0, no output; every assert holds.

CHECK YOUR UNDERSTANDING

Close the answer. Explain it.

Can const std::string& echo(const std::string& s) { return s; } make r in const auto& r = echo(std::string(3, 'x')); safe to use in the next statement?

Show a reference answer

No. After the temporary string binds to the parameter, it lasts only until the end of the full expression containing the call; returning the reference and rebinding r do not extend it again. Changing to std::string r = echo(std::string(3, 'x')); can copy the result while the temporary is still alive. A clearer design is for a function that produces a new result to return by value.

Check the sources

Drafts and official chapters change. The version mark is only the example’s minimum.

Back to the catalog