[dcl.type.cv]

# 9 Declarations [[dcl]](./#dcl)

## 9.2 Specifiers [[dcl.spec]](dcl.spec#dcl.type.cv)

### 9.2.9 Type specifiers [[dcl.type]](dcl.type#cv)

#### 9.2.9.2 The *cv-qualifier**s* [dcl.type.cv]

[1](#1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1266)

There are two [*cv-qualifier*](dcl.decl.general#nt:cv-qualifier "9.3.1 General [dcl.decl.general]")*s*, const andvolatile[.](#1.sentence-1)

Each [*cv-qualifier*](dcl.decl.general#nt:cv-qualifier "9.3.1 General [dcl.decl.general]") shall appear at most once in
a [*cv-qualifier-seq*](dcl.decl.general#nt:cv-qualifier-seq "9.3.1 General [dcl.decl.general]")[.](#1.sentence-2)

If a [*cv-qualifier*](dcl.decl.general#nt:cv-qualifier "9.3.1 General [dcl.decl.general]") appears in a[*decl-specifier-seq*](dcl.spec.general#nt:decl-specifier-seq "9.2.1 General [dcl.spec.general]"), the [*init-declarator-list*](dcl.decl.general#nt:init-declarator-list "9.3.1 General [dcl.decl.general]") or [*member-declarator-list*](class.mem.general#nt:member-declarator-list "11.4.1 General [class.mem.general]") of
the declaration shall not be empty[.](#1.sentence-3)

[*Note [1](#note-1)*:

[[basic.type.qualifier]](basic.type.qualifier "6.9.5 CV-qualifiers") and [[dcl.fct]](dcl.fct "9.3.4.6 Functions") describe how cv-qualifiers affect object and
function types[.](#1.sentence-4)

â *end note*]

Redundant cv-qualifications are ignored[.](#1.sentence-5)

[*Note [2](#note-2)*:

For example,
these could be introduced by typedefs[.](#1.sentence-6)

â *end note*]

[2](#2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1283)

[*Note [3](#note-3)*:

Declaring a variable const can affect its linkage ([[dcl.stc]](dcl.stc "9.2.2 Storage class specifiers"))
and its usability in constant expressions ([[expr.const]](expr.const "7.7 Constant expressions"))[.](#2.sentence-1)

As
described in [[dcl.init]](dcl.init "9.5 Initializers"), the definition of an object or subobject
of const-qualified type must specify an initializer or be subject to
default-initialization[.](#2.sentence-2)

â *end note*]

[3](#3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1292)

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[.](#3.sentence-1)

[*Note [4](#note-4)*:

Cv-qualifiers are supported by the type system so that they cannot be
subverted without [casting](expr.const.cast "7.6.1.11 Const cast [expr.const.cast]")[.](#3.sentence-2)

â *end note*]

[4](#4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1303)

Any attempt to modify ([[expr.assign]](expr.assign "7.6.19 Assignment and compound assignment operators"), [[expr.post.incr]](expr.post.incr "7.6.1.6 Increment and decrement"), [[expr.pre.incr]](expr.pre.incr "7.6.2.3 Increment and decrement")) a
const object ([[basic.type.qualifier]](basic.type.qualifier "6.9.5 CV-qualifiers")) during its
lifetime ([[basic.life]](basic.life "6.8.4 Lifetime")) results in undefined behavior[.](#4.sentence-1)

[*Example [1](#example-1)*: const int ci = 3; // cv-qualified (initialized as required) ci = 4; // error: attempt to modify constint i = 2; // not cv-qualifiedconst int* cip; // pointer to const int cip = &i; // OK, cv-qualified access path to unqualified*cip = 4; // error: attempt to modify through ptr to constint* ip;
ip = const_cast<int*>(cip); // cast needed to convert const int* to int**ip = 4; // defined: *ip points to i, a non-const objectconst int* ciq = new const int (3); // initialized as requiredint* iq = const_cast<int*>(ciq); // cast required*iq = 4; // undefined behavior: 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++; // error: 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 behavior: modifies a const subobject

â *end example*]

[5](#5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1346)

The semantics of an access through a volatile glvalue areimplementation-defined[.](#5.sentence-1)

If an attempt is made to access an object defined with a
volatile-qualified type through the use of a non-volatile glvalue,
the behavior is undefined[.](#5.sentence-2)

[6](#6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/declarations.tex#L1353)

[*Note [5](#note-5)*:

volatile is a hint to the implementation to avoid aggressive
optimization involving the object because it is possible for the value of the object
to change by means undetectable by an implementation[.](#6.sentence-1)

Furthermore, for some implementations, volatile can indicate that
special hardware instructions are needed to access the object[.](#6.sentence-2)

See [[intro.execution]](intro.execution "6.10.1 Sequential execution") for detailed semantics[.](#6.sentence-3)

In general, the
semantics of volatile are intended to be the same in C++ as
they are in C[.](#6.sentence-4)

â *end note*]
