This Exploration continues the examination of functions introduced in Exploration 20, by focusing on argument passing. Take a closer look. Remember that arguments are the expressions that you pass to a function in a function call. Parameters are the variables that you declare in the function declaration. This Exploration introduces the topic of function arguments, an area of C++ that is surprisingly complex and subtle.
Argument Passing
Function Arguments and Parameters
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
Expanding on these results, you may have noticed the modify function does not actually modify the variable a in main(), and the add function does not modify data. Your compiler might even have issued warnings to that effect.
As you can see, C++ passes arguments by value—that is, it copies the argument value to the parameter. The function can do whatever it wants with the parameter, but when the function returns, the parameter goes away, and the caller never sees any changes the function made.
If you want to return a value to the caller, use a return statement, as was done in the triple function.
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
Consider what would happen when you call add with a very large vector. The function makes an entirely new copy of its argument, consuming twice as much memory as it really ought to.
Pass-by-Reference
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
Pass Parameters by Reference
This time, the program modified the x parameter in modify and updated the vector’s contents in add.
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
_____________________________________________________________
The compiler does not allow you to call triple(14) when triple’s parameter is a reference. Consider what would happen if triple attempted to modify its parameter. You can’t assign to a number, only to a variable. Variables and literals fall into different categories of expressions. In general terms, a variable is an lvalue, as are references. A literal is called an rvalue, and expressions that are built up from operators and function calls usually result in rvalues. When a parameter is a reference, the argument in the function call must be an lvalue. If the parameter is call-by-value, you can pass an rvalue.
Can you pass an lvalue to a call-by-value parameter? ________________
You’ve seen many examples that you can pass an lvalue. C++ automatically converts any lvalue to an rvalue when it needs to. Can you convert an rvalue to an lvalue? ________________
If you aren’t sure, try to think of the problem in more concrete terms: can you convert an integer literal to a variable? That means you cannot convert an rvalue to an lvalue. Except, sometimes you can, as the next section will explain.
const References
Read the parameter declaration by starting at the parameter name and working your way from right to left. The parameter name is v; it is a reference; the reference is to a const object; and the object type is std::vector<int>. Sometimes, C++ can be hard to read, especially for a newcomer to the language, but with practice, you will soon read such declarations with ease.
is small. But with a more complicated declaration, such as the parameter to print_vector, the different style is more significant. I find my technique much easier to read and understand. My rule of thumb is to keep the const keyword as close as possible to whatever it is modifying.
More and more C++ programmers are coming around to adopt the const-near-the-name style instead of const out in front. Again, this is an opportunity for you to be in the vanguard of the most up-to-date C++ programming trends. But you have to get used to reading code with const out in front, because you’re going to see a lot of it.
The compiler stops you from modifying a const parameter.
Standard practice is to use references to pass any large data structure, such as vector, map, or string. If the function has no intention of making changes, declare the reference as a const. For small objects, such as int, use pass-by-value.
Convince yourself that you can pass an rvalue (such as 14) to triple. Thus, the more precise rule is that you can convert an rvalue to a const lvalue, but not to a non-const lvalue.
const_iterator
You can print the same results with a range-based for loop, but I wanted to show the use of const_iterator.
String Arguments
Strings present a unique opportunity. It is common practice to pass const strings to functions, but there is an unfortunate mismatch between C++ and its ancestor, C, when it comes to strings.
C lacks a built-in string type. Nor does it have any string type in its standard library. A quoted string literal is equivalent to a char array to which the compiler appends a NUL character (' ') to denote the end of the string. When a program constructs a C++ std::string from a quoted literal string, the std::string object must copy the contents of the string literal. What this means is that if a function declares a parameter with type std::string const& in order to avoid copying the argument, and the caller passes a string literal, that literal gets copied anyway.
In Exploration 20, calling prompted_read("Value: ") required constructing a std::string object, copying the string literal into that object, and then passing the object to the function. But the compiler can build and pass a string_view without copying any data. A string_view object is a lightweight read-only view of an existing string. You can usually treat a string_view the same way you would a const std::string. Use string_view whenever you want to pass a read-only string to a function; the function argument can be a quoted string literal, another string_view, or a std::string object. The only caveat to using string_view is that the standard library still has not caught on to string_view and there are many parts of the library that accept only string and not string_view. In this book, when you see strings passed as std::string const& instead of std::string_view, it is because the function must call some standard library function that does not handle string_view arguments.
Multiple Output Parameters
Now that you know how to pass strings, vectors, and whatnot to a function, you can begin to make further improvements to the word-counting program, as you will see in the next Exploration.