You are on page 1of 3

RETURN

PROGRAMMING POINTERS

Dan Saks

Top-Level cv-Qualifiers
in Function Parameters
For the past few months, Ive been void f(T volatile *); g(k);
discussing the role of cv-qualifiers (the
keywords const and volatile) appear- and this assortment of pointer copies argument k to parameter n
ing in function parameter declara- variables: without altering k. Even if you
tions. Ive been focusing on how you declared k as:
can overload functions whose parame- T *p;
ter types differ only in their use of cv- T const *pc; int const k = 1024;
qualifiers,1 and why you might want to
do so.2,3
Although cv-qualifiers that appear Although the const and volatile qualifiers usually
in a parameter declaration usually
become part of the functions signa- add valuable compile-time type information to
ture, theyre not always part of the
signature. There are times when C++ your programs, they dont always live up to all of
ignores cv-qualifiers in parameter
declarations as it determines a func- your expectations.
tions signature. In contrast, C never
ignores cv-qualifiers in parameter T volatile *pv;
declarations. This subtle difference the call g(k) would work just as well.
between C++ and C is my topic for the expression f(p) calls f(T *), f(pc) You can pass a constant argument as a
this month. calls f(T const *), and f(pv) calls nonconstant parameter, again because
f(T volatile *). passing by value just makes a copy of
Passing by value vs. by address The previous example illustrates the argument. In this example, para-
overloading with cv-qualifiers using meter n is nonconstant, so g can
When a C++ compiler encounters a functions that pass parameters by change ns value. However, changing
call to an overloaded function, it address. Lets see how the behavior ns value inside g has no effect on ks
selects the function to be called by changes when functions pass para- value because n doesnt refer back to k
matching the types of the actual argu- meters by value rather than by in any way.
ments in the call against the types of address. Changing gs declaration to:
the formal parameters in one of the For example, a function g declared
function declarations. The compilers as: int g(int const n);
overload resolution process often
takes cv-qualifiers into account. For int g(int n); has no effect on code that calls g. A
example, given three overloaded call such as g(k) still copies argument
functions: has a parameter n passed by value. A k to parameter n without altering k. It
call to g as in: doesnt matter whether k is constant.
void f(T *); You can always read the value of a con-
void f(T const *); int k; stant; you just cant write to it.
... Although declaring parameter n as

Embedded Systems Programming FEBRUARY 2000 63


PROGRAMMING POINTERS

constant doesnt matter to code that declared as: T *const p;


calls g, it does matter to code within g.
When n is nonconstant, g can use n as a int g(int n); declares p with type constant pointer
read-write variable, just as it can use any to T. Here, the const qualifier applies
nonconstant local variable. When n is Writing both of these declarations in only to the first level. In contrast,
constant, g cant alter the value in n. the same scope of a C++ program is
However, g can still copy n to a noncon- not an error. However, defining both T volatile *q;
stant local variable and perform compu- of these functions in the same pro-
tations in that local variable, as in: gram is an error, which might not be declares q with type pointer to volatile
reported until link time. T. Here, the volatile qualifier applies
int g(int const n) Here we see a difference between C only to the second level.
{ and C++. In C, declaring both: In C++, a cv-qualifier that applies to
int v = n; the first level of a type is called a top-
int g(int n); level cv-qualifier. For example, in:
// can alter v here int g(int const n);
T *const p;
} in the same scope is an error. C
never ignores cv-qualifiers in a func- the top-level cv-qualifier is const, and
Declaring a parameter passed by tion parameter declaration. In C, in:
value as constant may affect the func- these two gs have different function
tions implementation, but it doesnt types. The second declaration pro- T const *volatile q;
affect the functions outward behavior vokes a compile-time error because
as seen by any caller. C does not permit function the top-level cv-qualifier is volatile.
overloading. On the other hand:
Overloading
Top-level cv-qualifiers T const volatile *q;
The previous discussion raises a num-
ber of questions about what it means to In general, C++ does not include cv- has no top-level cv-qualifiers. In this
declare a pair of functions named g as: qualifiers in a functions signature case, the cv-qualifiers const and
when they appear at the top-level of volatile appear at the second level.
int g(int n); a parameter type. Heres a bit of back- Fundamental types such as char,
int g(int const n); ground to help you understand what I int, and double have only one level of
mean. composition. In a declaration such as:
Do you expect g(3) to call g(int) Types in C and C++ can have one or
or g(int const)? In other words, more levels of composition. For exam- int const n = 10;
should the call choose the g that can ple, p declared as:
alter its copy of 3, or the g that cannot the top-level cv-qualifier is const.
alter its copy? Or, is it an error to even T *p; Heres a more precise statement of
declare these functions in the same the way C++ treats cv-qualifiers in para-
scope? The two gs declared above has type pointer to T, which is a type meter types:
exhibit identical outward behavior. composed of two levels. The first level
Therefore, when a C++ compiler is pointer to and the second level is The signature of a function includes all cv-
encounters a call to g, it has no basis T. The declaration: qualifiers appearing in that functions
for preferring one g over the other. parameter types, except for those qualifiers
C++ avoids making the choice by T *f(int); appearing at the top-level of a parameter
treating both gs as the same g. type.
Specifically, the compiler ignores the declares f as a function returning
const qualifier in: pointer to T. This type has three lev- For example, in:
els. The first is function returning,
int g(int const n); the second is pointer to, and the int f(char const *p);
third isT.
as it determines the functions signa- Different cv-qualifiers can appear the const qualifier is not at the top
ture. Thus, the previous function has at different levels of composition. For level in the parameter declaration, so
the same signature as a function example: it is part of the functions signature.

64 FEBRUARY 2000 Embedded Systems Programming


PROGRAMMING POINTERS

On the other hand, in: and remains an active member. With Parameter Types, Embedded Systems
Thomas Plum, he wrote C++ Programming, September 1999, p. 77.
int f(char *const p); Programming Guidelines (Plum- 2. Saks, Dan, Overloading with const,
Hall). You can write to him at Embedded Systems Programming,
the const qualifier is at the top level, so dsaks@wittenberg.edu. December 1999, p. 81.
it is not part of the functions signa- 3. Saks, Dan, More on Overloading with
ture. This function has the same sig- References const, Embedded Systems
nature as: 1. Saks, Dan, Using const and volatile in Programming, January 2000, p. 71.

int f(char *p);

In a function declared as:

int f(char const *const p);

the const qualifier to the left of the *


is not at the top level, so it is part of
the functions signature. However, the
const qualifier to the right of the * is at
the top level, so it is not part of the
functions signature. Thus, the func-
tion declared just above has the same
signature as:

int f(char const *p);

Its important to note that C++ does


not ignore top-level cv-qualifiers in
object and type declarations. For
example, in declaring an object such
as:

port volatile *const p = ... ;

the top-level cv-qualifier is const. This


is not a parameter declaration, so all
cv-qualifiers are significant. The object
p is indeed constant.

More to come

Although C++ ignores top-level cv-


qualifiers in parameter declarations
when determining function signa-
tures, it does not ignore those cv-qual-
ifiers entirely. Ill explain what I mean
by that in my next column. esp

Dan Saks is the president of Saks &


Associates, a C/C++ training and con-
sulting company. He is also a contribut-
ing editor for the C/C++ Users
Journal. He served for many years as
secretary of the C++ standards committee

Embedded Systems Programming FEBRUARY 2000 65