C++ / a working model

85 / 163   ·   C++11   ·   11 min

auto is not equal to reference: checking the boundaries of copy and aliasing

Keep this sentence

auto deduces the type required by the declaration and does not automatically retain the reference properties of the initializer expression. Compare container copying, reference aliasing, and array decay, then use type assertions to fix the intent, avoiding mistaking “omitting the type name” for “saving the copy”.

In this lesson
  1. Look at the declaration form, not just the right-hand side of the equals sign
  2. Array decay is a difference that can be seen statically
  3. Place historical syntax explanations within the version
  4. Example
  5. Exercise
READING EVIDENCE / Partial text read

Overview of the New C++ (C++11/14)

Have fully read the official Artima 42-page sample (2 pages of title/copyright, Slides 1—40), including introduction, overview of copy and move, word frequency program C++98/11 comparison, auto, range-for, nullptr, enumerations, Unicode and raw strings. Uniform initialization, decltype, etc., only appear in the course catalog or descriptions and are not counted as actually read; the remaining content of the full 409-page course has not been obtained or read.

Edition, actual reading range, and original sources →

Look at the declaration form, not just the right-hand side of the equals sign

Given a const container, auto snapshot = source usually creates a new non-const container; const auto& view = source binds to the original object. Whether top-level const and reference information is retained depends on whether there are corresponding declaration modifiers on the left, and cannot be judged solely by whether the right side is a reference.

The example deliberately uses a modifiable original container: after copying, modifying snapshot leaves the original value unchanged; modifying the alias obtained through auto& affects the original value. These two assertions answer “who the operation acts on”, which is more useful than generally saying that auto is more modern. A const reference restricts this access path but does not freeze all other aliases.

Array decay is a difference that can be seen statically

Initializing an array with auto p = array yields a pointer, and the length is not in p's type; writing auto& whole = array retains the array reference. Directly verify both with is_same and static_assert, avoiding guessing the type based on coincidentally identical sizeof machine results.

Type checking and behavior checking are complementary: static assertions illustrate the nature of the interface, runtime assertions show that alias access can modify the same element. These rules are not auto's private magic; they are closely related to template parameter deduction; the course sample uses this connection to explain the costs and constraints behind concise syntax.

Place historical syntax explanations within the version

The sample that has been read was revised in 2015, covering from the beginning of the course to raw strings, which does not equal having read the entire training material. Its auto discussion is suitable for establishing a foundation, but when encountering details such as brace initialization and return type deduction, one must still consult the corresponding language version and subsequent defect reports.

This example deliberately does not rely on controversial historical brace deduction writings; the minimum requirement is C++11, and it still holds in C++20. For long-term stored references, one must separately check the lifetime of the source object: auto& will not extend the lifetime of ordinary lvalue objects for you, nor will it prevent container reallocation from invalidating element references.

Pitfalls

  • auto by-value can copy the entire container; a shorter type name does not mean the program allocates less.
  • const auto& is not ownership; after the original object is destroyed or the referenced target becomes invalid, the alias will still dangle.

Run an example

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

#include <cassert>
#include <type_traits>
#include <vector>
int main() {
    std::vector<int> source{2, 4};
    auto snapshot = source;
    auto& alias = source;
    const auto& view = source;
    snapshot[0] = 9;
    assert(source[0] == 2 && snapshot[0] == 9);
    alias[1] = 7;
    assert(source[1] == 7 && view[1] == 7);
    static_assert(std::is_same<decltype(snapshot), std::vector<int>>::value, "value copy");
    static_assert(std::is_same<decltype(view), const std::vector<int>&>::value, "const alias");
    int samples[3] = {1, 2, 3};
    auto pointer = samples;
    auto& whole = samples;
    static_assert(std::is_same<decltype(pointer), int*>::value, "array decays");
    static_assert(std::is_same<decltype(whole), int (&)[3]>::value, "bound retained");
    whole[2] = 8;
    assert(pointer[2] == 8 && samples[2] == 8);
}

Compile locally

g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread books-overview-new-cpp.cpp -o example && ./example

Expected result

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

CHECK YOUR UNDERSTANDING

Close the answer. Explain it.

If source is changed to a const vector, what are the types of auto snapshot and auto& alias respectively?

Show a reference answer

snapshot is still std::vector<int>, because by-value deduction strips the top-level const of the initializer type; alias is const std::vector<int>&, because reference deduction retains the const of the referenced object. Therefore snapshot[0] is writable, while alias[0] is not. One can delete the runtime statement that rewrites alias and add two is_same static assertions to verify these two types.

Check the sources

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

Back to the catalog