cv (const
和 volatile
) 类型限定符
出现在任何类型说明符中,包括声明语法的 decl-specifier-seq 中,以指定所声明对象或所命名类型的 constness 或 volatility。
- const - 定义类型是 *常量*。
- volatile - 定义类型是 *易变* 的。
目录 |
[编辑] 解释
除了函数类型或引用类型之外的任何(可能是不完整的)类型都属于以下四种不同但相关的类型组:
- 一个 *cv-unqualified* 版本。
- 一个 *const-qualified* 版本。
- 一个 *volatile-qualified* 版本。
- 一个 *const-volatile-qualified* 版本。
数组类型被认为具有与其元素类型相同的 cv-限定。
[编辑] const 和 volatile 对象
当一个对象首次创建时,所使用的 cv-限定符(可以是声明中 decl-specifier-seq 的一部分或 declarator 的一部分,或new-expression中 type-id 的一部分)决定了对象的 constness 或 volatility,如下所示:
- 一个 *const 对象* 是
- 类型为 const-qualified 的对象,或
- const 对象的非mutable子对象。
- 这样的对象不能被修改:直接修改会是一个编译时错误,间接修改(例如,通过非 const 类型的引用或指针修改 const 对象)会导致未定义行为。
- 一个 *volatile 对象* 是
- 类型为 volatile-qualified 的对象,
- volatile 对象的子对象,或
- const-volatile 对象的mutable子对象。
- 通过 volatile-qualified 类型 glvalue 表达式进行的每次访问(读写操作、成员函数调用等)都被视为优化目的的可见副作用(即,在单个执行线程中,volatile 访问不能被优化掉,也不能与在 volatile 访问之前或之后定序的另一个可见副作用重新排序。这使得 volatile 对象适用于与信号处理程序通信,但不适用于与另一个执行线程通信,请参阅std::memory_order)。任何通过非 volatile 类型的glvalue(例如,通过非 volatile 类型的引用或指针)访问 volatile 对象的尝试都会导致未定义行为。
- 一个 *const volatile 对象* 是
- 兼具 const 对象和 volatile 对象的行为。
每个 cv-限定符(const 和 volatile)在任何 cv-限定符序列中最多只能出现一次。例如,const const 和 volatile const volatile 不是有效的 cv-限定符序列。
[编辑] mutable
说明符
- mutable - 允许修改声明为 mutable 的类成员,即使包含对象声明为 const(即,类成员是可变的)。
可以出现在非引用非 const 类型的非静态类成员的声明中
class X { mutable const int* p; // OK mutable int* const q; // ill-formed mutable int& r; // ill-formed };
mutable 用于指定成员不影响类的外部可见状态(通常用于互斥锁、备忘缓存、延迟评估和访问检测)。
class ThreadsafeCounter { mutable std::mutex m; // The "M&M rule": mutable and mutex go together int data = 0; public: int get() const { std::lock_guard<std::mutex> lk(m); return data; } void inc() { std::lock_guard<std::mutex> lk(m); ++data; } };
[编辑] 转换
cv-限定符根据限制增加的顺序存在部分排序。类型可以被称为 *更* 或 *更少* cv-限定的:
- unqualified < const
- unqualified < volatile
- unqualified < const volatile
- const < const volatile
- volatile < const volatile
指向 cv-qualified 类型的引用和指针可以隐式转换为指向更 cv-qualified 类型的引用和指针,详见限定符转换。
要将指向 cv-qualified 类型的引用或指针转换为指向较少 cv-qualified 类型的引用或指针,必须使用const_cast
。
[编辑] 注意
在非局部非 volatile 非模板(C++14 起)非inline(C++17 起) 变量的声明中使用 const 限定符,如果该变量未声明为 extern,则它具有内部链接。这与 C 不同,C 中 const 文件作用域变量具有外部链接。
C++ 语言语法将 mutable 视为存储类说明符,而不是类型限定符,但它不影响存储类或链接。
某些 volatile 的用法已被弃用 |
(C++20 起) |
[编辑] 关键字
[编辑] 示例
#include <cstdlib> int main() { int n1 = 0; // non-const object const int n2 = 0; // const object int const n3 = 0; // const object (same as n2) volatile int n4 = 0; // volatile object const struct { int n1; mutable int n2; } x = {0, 0}; // const object with mutable member n1 = 1; // OK: modifiable object // n2 = 2; // error: non-modifiable object n4 = 3; // OK: treated as a side-effect // x.n1 = 4; // error: member of a const object is const x.n2 = 4; // OK: mutable member of a const object isn't const const int& r1 = n1; // reference to const bound to non-const object // r1 = 2; // error: attempt to modify through reference to const const_cast<int&>(r1) = 2; // OK: modifies non-const object n1 const int& r2 = n2; // reference to const bound to const object // r2 = 2; // error: attempt to modify through reference to const // const_cast<int&>(r2) = 2; // undefined behavior: attempt to modify const object n2 [](...){}(n3, n4, x, r2); // see also: [[maybe_unused]] std::system("g++ -O3 -Wa,-adhln ./main.cpp"); // may issue asm on POSIX systems }
可能的输出
# typical machine code produced on an x86_64 platform # (only the code that contributes to observable side-effects is emitted) main: movl $0, -4(%rsp) # volatile int n4 = 0; movl $3, -4(%rsp) # n4 = 3; xorl %eax, %eax # return 0 (implicit) ret
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 1428 | C++98 | “const 对象”的定义基于声明 | 基于对象类型 |
CWG 1528 | C++98 | 对同一 cv-限定符序列中每个 cv-限定符的出现次数没有要求 对同一 cv-限定符序列中每个 cv-限定符的出现次数没有要求 |
最多一次 每个 cv-限定符 |
CWG 1799 | C++98 | mutable 可以应用于未声明为 const 的数据成员 const,但成员类型仍可以是 const-qualified |
不能将 mutable 应用于 const-qualified 类型的数据成员 const-qualified 类型的数据成员 |
[编辑] 另见
C 文档 关于 const 限定符
| |
C 文档 关于 volatile 限定符
|