FAQs in section [18]:
[18.1] What is "const correctness"?
A good thing. It means using the keyword const to prevent const objects
from getting mutated.
For example, if you wanted to create a function f() that accepted a std::string,
plus you want to promise callers not to change the caller's std::string that gets
passed to f(), you can have f() receive its std::string parameter...
- void f1(const std::string& s);
// Pass by reference-to-const
- void f2(const std::string* sptr);
// Pass by pointer-to-const
- void f3(std::string s);
// Pass by value
In the pass by reference-to-const and pass by
pointer-to-const cases, any attempts to change to the caller's std::string
within the f() functions would be flagged by the compiler as an error at
compile-time. This check is done entirely at compile-time: there is no
run-time space or speed cost for the const. In the pass by value
case (f3()), the called function gets a copy of the caller's std::string. This
means that f3() can change its local copy, but the copy is destroyed when
f3() returns. In particular f3() cannot change the caller's std::string
object.
As an opposite example, if you wanted to create a function g() that accepted
a std::string, but you want to let callers know that g() might change the
caller's std::string object. In this case you can have g() receive its std::string
parameter...
- void g1(std::string& s);
// Pass by reference-to-non-const
- void g2(std::string* sptr);
// Pass by pointer-to-non-const
The lack of const in these functions tells the compiler that they are allowed
to (but are not required to) change the caller's std::string object. Thus they
can pass their std::string to any of the f() functions, but only f3() (the one
that receives its parameter "by value") can pass its std::string to g1() or
g2(). If f1() or f2() need to call either g() function, a local copy
of the std::string object must be passed to the g() function; the parameter to
f1() or f2() cannot be directly passed to either g() function. E.g.,
void g1(std::string& s);
void f1(const std::string& s)
{
g1(s); // Compile-time Error since s is const
std::string localCopy = s;
g1(localCopy); // OK since localCopy is not const
}
Naturally in the above case, any changes that g1() makes are made to the
localCopy object that is local to f1(). In particular, no changes
will be made to the const parameter that was passed by reference to f1().
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.2] How is "const correctness" related to ordinary type safety?
Declaring the const-ness of a parameter is just another form of type safety.
It is almost as if a const std::string, for example, is a different class than
an ordinary std::string, since the const variant is missing the various mutative
operations in the non-const variant (e.g., you can imagine that a const
std::string simply doesn't have an assignment operator).
If you find ordinary type safety helps you get systems correct (it does;
especially in large systems), you'll find const correctness helps also.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.3] Should I try to get things const correct "sooner" or "later"?
At the very, very, very beginning.
Back-patching const correctness results in a snowball effect: every const
you add "over here" requires four more to be added "over there."
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.4] What does "const Fred* p" mean?
It means p points to an object of class Fred, but p can't be used to
change that Fred object (naturally p could also be NULL).
For example, if class Fred has a const member
function called inspect(), saying p->inspect() is OK.
But if class Fred has a non-const member
function called mutate(), saying p->mutate() is an error
(the error is caught by the compiler; no run-time tests are done, which means
const doesn't slow your program down).
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.5] What's the difference between "const Fred* p", "Fred* const p" and "const Fred* const p"?
You have to read pointer declarations right-to-left.
- const Fred* p means "p points to a Fred that is const"
that is, the Fred object can't be changed via
p.
- Fred* const p means "p is a const pointer to a Fred"
that is, you can change the Fred object via
p, but you can't change the pointer p itself.
- const Fred* const p means "p is a const pointer to a
const Fred" that is, you can't change the pointer p itself, nor can
you change the Fred object via
p.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.6] What does "const Fred& x" mean?
It means x aliases a Fred object, but x can't be used to change that
Fred object.
For example, if class Fred has a const member
function called inspect(), saying x.inspect() is OK. But
if class Fred has a non-const member function
called mutate(), saying x.mutate() is an error (the error is
caught by the compiler; no run-time tests are done, which means const doesn't
slow your program down).
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.7] Does "Fred& const x" make any sense?
No, it is nonsense.
To find out what the above declaration means, you
have to read it right-to-left. Thus "Fred& const x" means "x is
a const reference to a Fred". But that is redundant, since references are
always const. You can't reseat a reference.
Never. With or without the const.
In other words, "Fred& const x" is functionally equivalent to
"Fred& x". Since you're gaining nothing by adding the const after
the &, you shouldn't add it since it will confuse people. I.e., the
const will make some people think that the Fred is const, as if you had
said "const Fred& x".
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.8] What does "Fred const& x" mean?
Fred const& x is functionally equivalent to
const Fred& x. However, the real question is
which should be used.
Answer: absolutely no one should pretend they can make decisions for
your organization until they know something about your organization.
One size does not fit all; there is no "right" answer for all organizations,
so do not allow anyone to make a knee-jerk decision in either
direction. "Think" is not a four-letter word.
For example, some organizations value consistency and have tons of code using
const Fred&; for those, Fred const& would be a bad decision
independent of its merits. There are lots of other business scenarios, some
of which produce a preference for Fred const&, others a preference for
const Fred&.
Use a style that is appropriate for your organization's average
maintenance programmer. Not the gurus, not the morons, but the average
maintenance programmer. Unless you're willing to fire them and hire new ones,
make sure that they understand your code. Make a business decision
based on your realities, not based on someone else's assumptions.
You'll need to overcome a little inertia to go with Fred const&. Most
current C++ books use const Fred&, most programmers learned C++ with
that syntax, and most programmers still use that syntax. That doesn't mean
const Fred& is necessarily better for your organization, but it does
mean you may get some confusion and mistakes during the transition and/or when
you integrate new people. Some organizations are convinced the benefits of
Fred const& outweigh the costs; others, apparently, are not.
Another caveat: if you decide to use Fred const& x, do something to
make sure your people don't mis-type it as the
nonsensical "Fred& const x".
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.9] What does "Fred const* x" mean?
Fred const* x is functionally equivalent to
const Fred* x. However, the real question is
which should be used.
Answer: absolutely no one should pretend they can make decisions for
your organization until they know something about your organization.
One size does not fit all; there is no "right" answer for all organizations,
so do not allow anyone to make a knee-jerk decision in either
direction. "Think" is not a four-letter word.
For example, some organizations value consistency and have tons of code using
const Fred*; for those, Fred const* would be a bad decision
independent of its merits. There are lots of other business scenarios, some
of which produce a preference for Fred const*, others a preference for
const Fred*.
Use a style that is appropriate for your organization's average
maintenance programmer. Not the gurus, not the morons, but the average
maintenance programmer. Unless you're willing to fire them and hire new ones,
make sure that they understand your code. Make a business decision
based on your realities, not based on someone else's assumptions.
You'll need to overcome a little inertia to go with Fred const*. Most
current C++ books use const Fred*, most programmers learned C++ with
that syntax, and most programmers still use that syntax. That doesn't mean
const Fred* is necessarily better for your organization, but it does
mean you may get some confusion and mistakes during the transition and/or when
you integrate new people. Some organizations are convinced the benefits of
Fred const* outweigh the costs; others, apparently, are not.
Another caveat: if you decide to use Fred const* x, do something to
make sure your people don't mis-type it as the
semantically different but syntactically similar "Fred* const
x". Those two forms have completely
different meanings even though they look similar at first blush.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.10] What is a "const member function"?
A member function that inspects (rather than mutates) its object.
A const member function is indicated by a const suffix just after the
member function's parameter list. Member functions with a const suffix are
called "const member functions" or "inspectors." Member functions without a
const suffix are called "non-const member functions" or "mutators."
class Fred {
public:
void inspect() const; // This member promises NOT to change *this
void mutate(); // This member function might change *this
};
void userCode(Fred& changeable, const Fred& unchangeable)
{
changeable.inspect(); // OK: doesn't change a changeable object
changeable.mutate(); // OK: changes a changeable object
unchangeable.inspect(); // OK: doesn't change an unchangeable object
unchangeable.mutate(); // ERROR: attempt to change unchangeable object
}
The error in unchangeable.mutate() is caught at compile time. There is
no runtime space or speed penalty for const.
The trailing const on inspect() member function means that the
abstract (client-visible) state of the object isn't going to change.
This is slightly different from promising that the "raw bits" of the object's
struct aren't going to change. C++ compilers aren't allowed to take the
"bitwise" interpretation unless they can solve the aliasing problem, which
normally can't be solved (i.e., a non-const alias could exist which could
modify the state of the object). Another (important) insight from this
aliasing issue: pointing at an object with a pointer-to-const doesn't guarantee
that the object won't change; it promises only that the object won't change
via that pointer.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.11] What's the relationship between a return-by-reference and a const member function?
If you want to return a member of your this object by reference from
an inspector method, you should return it using
reference-to-const, that is, const X&.
class Person {
public:
const std::string& name_good() const; ← Right: the caller can't change the name
std::string& name_evil() const; ← Wrong: the caller can change the name
...
};
void myCode(const Person& p) ← You're promising not to change the Person object...
{
p.name_evil() = "Igor"; ← ...but you changed it anyway!!
}
The good news is that the compiler will often catch you if you get
this wrong. In particular, if you accidentally return a member of your
this object by non-const reference, such as in
Person::name_evil() above, the compiler will often detect it
and give you a compile-time error while compiling the innards of, in this
case, Person::name_evil().
The bad news is that the compiler won't always catch you: there are some cases
where the compiler simply won't ever give you a compile-time error message.
Net: you need to think, and you need to remember the guideline in this FAQ.
If the thing you are returning by reference is logically part of your
this object, independent of whether it is physically embedded within
your this object, then a const method needs to return by
const reference or by value, but not by non-const reference.
(The idea of "logically" part of your this object is related to the
notion of an object's "abstract state"; see the
previous FAQ for more.)
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.12] What's the deal with "const-overloading"?
It's when you have an inspector method and a
mutator method with the same name and the same
number and type of parameters the methods differ only in that one is
const and the other is non-const.
The subscript operator is a common use of const-overloading. You
should generally try to use one of the standard container
templates, such as std::vector, but if you need to create your
own class that has a subscript operator, here's the rule of thumb:
subscript operators often come in pairs.
class Fred { ... };
class MyFredList {
public:
const Fred& operator[] (unsigned index) const; ← subscript operators often come in pairs
Fred& operator[] (unsigned index); ← subscript operators often come in pairs
...
};
When you apply the subscript operator to a MyFredList object that is
non-const, the compiler will call the non-const subscript
operator. Since that returns a normal Fred&, you can both inspect and
mutate the corresponding Fred object. For example, suppose class
Fred has an inspector called Fred::inspect() const and a
mutator Fred::mutate():
void f(MyFredList& a) ← the MyFredList is non-const
{
// Okay to call methods that DON'T change the Fred at a[3]:
Fred x = a[3];
a[3].inspect();
// Okay to call methods that DO change the Fred at a[3]:
Fred y;
a[3] = y;
a[3].mutate();
}
However when you apply the subscript operator to a const MyFredList
object, the compiler will call the const subscript operator. Since
that returns a const Fred&, you can inspect the corresponding
Fred object, but you can't mutate/change it:
void f(const MyFredList& a) ← the MyFredList is const
{
// Okay to call methods that DON'T change the Fred at a[3]:
Fred x = a[3];
a[3].inspect();
// Error (fortunately!) if you try to change the Fred at a[3]:
Fred y;
a[3] = y; ← Fortunately(!) the compiler catches this error at compile-time
a[3].mutate(); ← Fortunately(!) the compiler catches this error at compile-time
}
Const overloading for subscript- and funcall-operators is illustrated in FAQ
[13.10], [16.17],
[16.18], [16.19], and
[35.2].
You can, of course, also use const-overloading for things other than
the subscript operator.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.13] What do I do if I want a const member function to make an "invisible" change to a data member?
Use mutable (or, as a last resort, use const_cast).
A small percentage of inspectors need to make innocuous changes to data members
(e.g., a Set object might want to cache its last lookup in hopes of improving
the performance of its next lookup). By saying the changes are "innocuous," I
mean that the changes wouldn't be visible from outside the object's interface
(otherwise the member function would be a mutator rather than an inspector).
When this happens, the data member which will be modified should be marked as
mutable (put the mutable keyword just before the data member's declaration;
i.e., in the same place where you could put const). This tells the compiler
that the data member is allowed to change during a const member function. If
your compiler doesn't support the mutable keyword, you can cast away the
const'ness of this via the const_cast keyword (but see the
NOTE below before doing this). E.g., in Set::lookup()
const, you might say,
Set* self = const_cast<Set*>(this);
// See the NOTE below before doing this!
After this line, self will have the same bits as this (e.g., self
== this), but self is a Set* rather than a const Set*
(technically a const Set* const, but the right-most const is
irrelevant to this discussion). Therefore you can use self to modify
the object pointed to by this.
NOTE: there is an extremely unlikely error that can occur with
const_cast. It only happens when three very rare things are combined at the
same time: a data member that ought to be mutable (such as is discussed
above), a compiler that doesn't support the mutable keyword, and an object
that was originally defined to be const (as opposed to a normal, non-const
object that is pointed to by a pointer-to-const). Although this combination
is so rare that it may never happen to you, if it ever did happen the code may
not work (the Standard says the behavior is undefined).
If you ever want to use const_cast, use mutable instead. In other words,
if you ever need to change a member of an object, and that object is pointed to
by a pointer-to-const, the safest and simplest thing to do is add mutable
to the member's declaration. You can use const_cast if you are sure
that the actual object isn't const (e.g., if you are sure the object is
declared something like this: Set s;), but if the object itself
might be const (e.g., if it might be declared like: const Set
s;), use mutable rather than const_cast.
Please don't write and tell me that version X of compiler Y on
machine Z allows you to change a non-mutable member of a const
object. I don't care it is illegal according to the language and your code
will probably fail on a different compiler or even a different version (an
upgrade) of the same compiler. Just say no. Use mutable instead.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.14] Does const_cast mean lost optimization opportunities?
In theory, yes; in practice, no.
Even if the language outlawed const_cast, the only way to avoid flushing the
register cache across a const member function call would be to solve the
aliasing problem (i.e., to prove that there are no non-const pointers that
point to the object). This can happen only in rare cases (when the object is
constructed in the scope of the const member function invocation, and when
all the non-const member function invocations between the object's
construction and the const member function invocation are statically bound,
and when every one of these invocations is also inlined, and when the
constructor itself is inlined, and when any member functions the constructor
calls are inline).
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.15] Why does the compiler allow me to change an int after I've pointed at it with a const int*?
Because "const int* p" means "p promises not to change the *p,"
not "*p promises not to change."
Causing a const int* to point to an int doesn't const-ify the int. The
int can't be changed via the const int*, but if someone else has an int*
(note: no const) that points to ("aliases") the same int, then that int*
can be used to change the int. For example:
void f(const int* p1, int* p2)
{
int i = *p1; // Get the (original) value of *p1
*p2 = 7; // If p1 == p2, this will also change *p1
int j = *p1; // Get the (possibly new) value of *p1
if (i != j) {
std::cout << "*p1 changed, but it didn't change via pointer p1!\n";
assert(p1 == p2); // This is the only way *p1 could be different
}
}
int main()
{
int x = 5;
f(&x, &x); // This is perfectly legal (and even moral!)
...
}
Note that main() and f(const int*,int*) could be in different
compilation units that are compiled on different days of the week. In that
case there is no way the compiler can possibly detect the aliasing at compile
time. Therefore there is no way we could make a language rule that prohibits
this sort of thing. In fact, we wouldn't even want to make such a rule, since
in general it's considered a feature that you can have many pointers pointing
to the same thing. The fact that one of those pointers promises not to change
the underlying "thing" is just a promise made by the pointer; it's
not a promise made by the "thing".
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.16] Does "const Fred* p" mean that *p can't change?
No! (This is related to the FAQ about aliasing of
int pointers.)
"const Fred* p" means that the Fred can't be changed via pointer p,
but there might be other ways to get at the object without going through a
const (such as an aliased non-const pointer such as a Fred*). For
example, if you have two pointers "const Fred* p" and "Fred* q"
that point to the same Fred object (aliasing), pointer q can be used to
change the Fred object but pointer p cannot.
class Fred {
public:
void inspect() const; // A const member function
void mutate(); // A non-const member function
};
int main()
{
Fred f;
const Fred* p = &f;
Fred* q = &f;
p->inspect(); // OK: No change to *p
p->mutate(); // Error: Can't change *p via p
q->inspect(); // OK: q is allowed to inspect the object
q->mutate(); // OK: q is allowed to mutate the object
f.inspect(); // OK: f is allowed to inspect the object
f.mutate(); // OK: f is allowed to mutate the object
...
}
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
[18.17] Why am I getting an error converting a Foo** → const Foo**?
Because converting Foo** → const Foo** would be
invalid and dangerous.
C++ allows the (safe) conversion Foo* → const
Foo*, but gives an error if you try to implicitly convert Foo**
→ const Foo**.
The rationale for why that error is a good thing is given below. But first,
here is the most common solution: simply change const Foo** to
const Foo* const*:
class Foo { /* ... */ };
void f(const Foo** p);
void g(const Foo* const* p);
int main()
{
Foo** p = /*...*/;
...
f(p); // ERROR: it's illegal and immoral to convert Foo** to const Foo**
g(p); // OK: it's legal and moral to convert Foo** to const Foo* const*
...
}
The reason the conversion from Foo** → const Foo**
is dangerous is that it would let you silently and accidentally modify a
const Foo object without a cast:
class Foo {
public:
void modify(); // make some modify to the this object
};
int main()
{
const Foo x;
Foo* p;
const Foo** q = &p; // q now points to p; this is (fortunately!) an error
*q = &x; // p now points to x
p->modify(); // Ouch: modifies a const Foo!!
...
}
Reminder: please do not pointer-cast your way around this.
Just Say No!
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Mar 1, 2006