Concepts / References in C++

References in C++

A const reference lets a function read an argument without copying it, while forbidding changes to the original variable through that name.

  • Programming

A Function That Only Reads Its Input

Many functions only need to look at the data they receive. A function may compute a total, search for a value, or print a report, and it never needs to change the caller's data. Even so, the way an argument is passed is a real design decision, because it determines both what the function is allowed to do to the argument and how much work the call performs.

python
Notes on the code
  • The parameter is a const reference to a vector of doubles.
  • The loop reads each element and adds it to a running total.
  • The function returns the total and never changes values.

What do you think happens?

Suppose someone edits the body of sum so that it calls values.push_back(1.0) before the loop. Will the function compile?

  • Yes, it compiles and the total includes the extra 1.0

  • No, the compiler rejects the modifying line

Reveal answer
Answer:

No, the compiler rejects the modifying line

The parameter is a const reference, so the rule that forbids changes through this name is enforced at the line that tries to modify the vector. Reading the elements in the loop would still compile fine.

Copy Cost Versus Mutation Power

The const reference comes from a tradeoff between two goals. The first goal is to keep the function from changing the argument. The second is to avoid the cost of copying. Passing by value gives the function its own copy, which protects the caller's data but costs the work of duplicating it. Passing by non-const reference avoids that copying, but by itself it lets the function modify the original. Adding const to the reference keeps the saving and removes the ability to change the data.

Copy madeduplicates the dataNo copyuses caller's dataNo copyuses caller's dataCaller protectedfunction changes the copyonlyChanges reach calleroriginal can be modifiedChanges blockedenforced by the compiler
For a read-only function, what does each passing style cost in copying, and what guarantee does each give the caller about the argument?

What the Const Reference Actually Holds

A reference parameter does not hold a separate copy of the vector. The parameter v is another name for the caller's vector, so reads go through to the caller's data. Writes through v are blocked at compile time. The const reference is therefore a promise enforced by the language rather than a convention the programmer has to remember.

caller's vectororiginal dataconst reference valias to caller's vectorby-value parameterits own parameterseparate copy ofelementsduplicated datarefers toowns
When a function receives a const reference, does it hold a separate copy of the vector, or does it refer to the same storage as the caller's variable?

Reading the Declaration

The source describes a declaration of the form double sum(const vector<double>&). Each part of that line has a job. The keyword const says the referred-to object cannot be modified through this name. The type vector<double> names what is being referred to. The ampersand makes the parameter a reference rather than a copy.

doublereturn type of the functionsumfunction nameconstreferred-to object cannotbe modified through thisnamevector<double>the type being referred to&declarator: makes theparameter a reference
What does each token in the declaration do, and which part controls whether the argument can be modified through its name?

Finding the Line That Breaks

When a const reference version of a function fails, the fastest route is to find the exact line where the behavior stops matching what you intended. Here the intended result is the total of the elements. Consider an edited version of sum that tries to change the vector it was given.

python
Notes on the code
  • The push_back call is the only statement that modifies the parameter.
  • The loop only reads values, so it is allowed through a const reference.
  • The return statement behaves the same as before the edit.
Worked example

Locating the failing statement

A function sum takes const vector<double>& values. Its body modifies values before summing. Which statement breaks the const rule, and why does the loop still compile?

  1. 1
    Identify the restricted name

    The parameter values is declared const, so the referred-to vector cannot be modified through the name values.

  2. 2
    Check each statement for modification

    The declaration of total only creates a local double. The push_back call tries to change the vector, which is a modification through values.

  3. 3
    Check reads

    The range-based for loop reads each element and copies its value into x. Reading does not modify the vector, so the loop is allowed.

  4. 4
    Locate the divergence

    The failure is at the modifying line. The loop and the return statement behave as before, so they are not the cause.

Result

The push_back line is the one that breaks the const rule. The compiler rejects it, and the function does not build.

Statement in bodyModifies theparameter?Compilesreads such as the loop andreturnCompile errorat the modifying linechecknoyes
Given a function body that modifies or only reads a const reference parameter, which statement breaks the rule, and why does reading the same parameter compile fine?

Choosing a Passing Style

Once the mechanics are clear, the design choice is usually straightforward. If a function only reads its argument and copying would be costly, a const reference expresses both facts in the signature. If the function must modify the caller's data, a non-const reference is the appropriate tool. If the function needs its own working copy that it may change freely, passing by value gives it one, at the cost of duplicating the data.

Passing styleCopy madeCan the function modify the caller's data?Caller's data protected
By valueYes, the argument is duplicatedNo, it only changes its own copyYes
By non-const referenceNoYesNo
By const referenceNoNo, changes through the name are blockedYes, through this name

Trade-offs for a read-only function argument

Names That Can Still Change the Vector

A const reference forbids changes only through that name. It does not make the original object constant. If the caller still has a non-const name for the same vector, that name remains able to modify the vector. The restriction belongs to the name that was declared const, not to the object it refers to.

vector of doublesthe one objectvalues (caller)non-const namev (const reference)const namecan modifyread only
If the caller still has a non-const name for the same vector, can that name change it, and which names are allowed to modify it?

Common Mistakes With Const References

  • Common mistake

    Believing that const makes the original vector constant everywhere

    What it looks like
    vector<double> values = {1.0, 2.0};  sum(values); values.push_back(3.0);
    Why it fails

    The const applies to the name the function uses. The caller's non-const name can still modify the vector.

    Do this instead

    Treat const on a reference parameter as a restriction on that name only.

  • Common mistake

    Searching for the compile error in the loop or the return statement

    Why it fails

    The loop and return only read the parameter, which is allowed through a const reference.

    Do this instead

    Find the statement that modifies the parameter, such as a push_back or an assignment to an element.

  • Common mistake

    Passing a read-only argument by value out of habit

    Why it fails

    Passing by value makes the function work on its own copy, which costs the work of duplicating the data.

    Do this instead

    When the function only reads and the argument is large, use a const reference.

  • Common mistake

    Dropping the ampersand and assuming the parameter is still a reference

Practice: Predict, Read, and Choose

EASY

A function is declared as double average(const vector<double>& data). Its body computes the sum of data and divides by data.size(). Does this body compile? Explain which statement, if any, breaks the const rule.

Hints
  • Check every statement for a call or assignment that modifies data.
  • Reading an element or calling size() does not modify the vector.
MEDIUM

Write the signature of a function reverse_copy that reads a vector of doubles and returns a new vector with the elements in reverse order. The function must not modify the caller's vector. Explain what each part of your declaration does, and say which declarator operator you used.

Hints
  • The parameter is read-only, so consider a const reference.
  • Read the declaration from the name outward to check that the reference applies to the whole const type.
MEDIUM

A teammate wants a function that adds 1.0 to every element of its argument and then returns the total. They propose double add_and_sum(const vector<double>& values). Explain why this will not compile and state the passing style that would let the function modify the caller's vector.

Hints
  • Find the statement that assigns to an element or calls push_back.
  • A non-const reference lets the function change the caller's data.

Key Takeaways

  • A const reference lets a function read an argument without copying it, while forbidding changes to the original through that name.
  • In a declaration such as double sum(const vector<double>&), const restricts modification through the name, vector<double> names the referred-to type, and the ampersand makes the parameter a reference rather than a copy.
  • A const reference violation is found at the statement that modifies the parameter, while reads in the loop and return compile normally.
  • Pass by value for a private working copy, by non-const reference when the function must change the caller's data, and by const reference when it only reads large data.

Related concepts

More concepts starting with R