7 Declarations [dcl.dcl]

7.1 Specifiers [dcl.spec]

7.1.6 Type specifiers [dcl.type]

7.1.6.1 The cv-qualifiers [dcl.type.cv]

There are two cv-qualifiers, const and volatile. Each cv-qualifier shall appear at most once in a cv-qualifier-seq. If a cv-qualifier appears in a decl-specifier-seq, the init-declarator-list of the declaration shall not be empty. [ Note: [basic.type.qualifier] and [dcl.fct] describe how cv-qualifiers affect object and function types.  — end note ] Redundant cv-qualifications are ignored. [ Note: For example, these could be introduced by typedefs. — end note ]

Note: Declaring a variable const can affect its linkage ([dcl.stc]) and its usability in constant expressions ([expr.const]). As described in [dcl.init], the definition of an object or subobject of const-qualified type must specify an initializer or be subject to default-initialization.  — end note ]

A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [ Note: Cv-qualifiers are supported by the type system so that they cannot be subverted without casting ([expr.const.cast]).  — end note ]

Except that any class member declared mutable ([dcl.stc]) can be modified, any attempt to modify a const object during its lifetime ([basic.life]) results in undefined behavior. [ Example:

const int ci = 3;               // cv-qualified (initialized as required)
ci = 4;                         // ill-formed: attempt to modify const

int i = 2;                      // not cv-qualified
const int* cip;                 // pointer to const int
cip = &i;                       // OK: cv-qualified access path to unqualified
*cip = 4;                       // ill-formed: attempt to modify through ptr to const

int* ip;
ip = const_cast<int*>(cip);     // cast needed to convert const int* to int*
*ip = 4;                        // defined: *ip points to i, a non-const object

const int* ciq = new const int (3);     // initialized as required
int* iq = const_cast<int*>(ciq);        // cast required
*iq = 4;                                // undefined: modifies a const object

For another example

struct X {
  mutable int i;
  int j;
};
struct Y {
  X x;
  Y();
};

const Y y;
y.x.i++;                        // well-formed: mutable member can be modified
y.x.j++;                        // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y);      // cast away const-ness of y
p->x.i = 99;                    // well-formed: mutable member can be modified
p->x.j = 99;                    // undefined: modifies a const member

 — end example ]

What constitutes an access to an object that has volatile-qualified type is implementation-defined. If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.

Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C.  — end note ]