cv
(
const
and
volatile
)
type qualifiers
出现在任何类型说明符中,包括 声明语法 的 decl-specifier-seq ,用于指定被声明对象的常量性或易变性,或指定被命名类型的这些属性。
- const - 定义该类型为 常量 。
- volatile - 定义该类型为 易变 。
目录 |
说明
除 函数类型 或 引用类型 外,任何(可能为 不完整 的)类型都属于以下四种不同但相关的类型组之一:
- 一个 非cv限定 的版本。
- 一个 const限定 的版本。
- 一个 volatile限定 的版本。
- 一个 const-volatile限定 的版本。
数组类型 被认为与其元素类型具有相同的 cv 限定。
const 与 volatile 对象
当对象首次创建时,所使用的cv限定符(可能属于 声明说明符序列 、 声明 中的 声明符 部分,或是 new表达式 中 类型标识 的组成部分)将按以下方式决定对象的常量性或易变性:
- 一个 const 对象 是
-
- 类型被 const 限定的对象,或
- const 对象的非 mutable 子对象。
- 此类对象不可被修改:尝试直接修改会导致编译时错误,而尝试间接修改(例如通过非 const 类型的引用或指针修改 const 对象)将导致未定义行为。
- 一个 volatile 对象 是
-
- 其类型为 volatile 限定的对象,
- volatile 对象的子对象,或
- const-volatile 对象的 mutable 子对象。
- 通过 volatile 限定类型的泛左值表达式进行的每次访问(读写操作、成员函数调用等)均被视为 优化目的 下的可见副作用(即在单线程执行中,volatile 访问不能被优化消除,也不能与在 顺序先后 关系中位于该 volatile 访问之前或之后的另一个可见副作用重新排序)。这使得 volatile 对象适用于与 信号处理程序 通信,但不适用于与其他执行线程通信(参见 std::memory_order )。任何通过非 volatile 类型的 泛左值 (例如通过非 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; // 正确 mutable int* const q; // 非法声明 mutable int& r; // 非法声明 };
mutable 用于指定该成员不影响类的外部可见状态(通常用于互斥锁、备忘录缓存、延迟求值和访问检测等场景)。
class ThreadsafeCounter { mutable std::mutex m; // M&M规则:mutable与mutex需同时使用 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
标签内的C++术语均未翻译,仅翻译了普通文本部分。将"unqualified"译为"无限定符"是C++类型限定符的标准译法)
对 cv 限定类型的引用和指针可以隐式转换为对更多 cv 限定的类型的引用和指针,详见 限定转换 。
要将引用或指针转换为具有较少 cv 限定符类型的引用或指针,必须使用
const_cast
。
注释
在声明非局部非易失性 非 模板 (C++14 起) 非 内联 (C++17 起) 变量时使用 const 限定符(且未声明为 extern )会使其具有 内部链接 。这与 C 语言不同,在 C 语言中 const 文件作用域变量具有外部链接。
C++ 语言语法将 mutable 视为 存储类说明符 ,而非类型限定符,但它不影响存储类别或链接。
|
volatile 的某些用法已被弃用: |
(since C++20) |
关键词
示例
#include <cstdlib> int main() { int n1 = 0; // 非常量对象 const int n2 = 0; // 常量对象 int const n3 = 0; // 常量对象(与 n2 相同) volatile int n4 = 0; // 易变对象 const struct { int n1; mutable int n2; } x = {0, 0}; // 带有 mutable 成员的常量对象 n1 = 1; // 正确:可修改对象 // n2 = 2; // 错误:不可修改对象 n4 = 3; // 正确:被视为副作用 // x.n1 = 4; // 错误:常量对象的成员是常量 x.n2 = 4; // 正确:常量对象的 mutable 成员不是常量 const int& r1 = n1; // 绑定到非常量对象的常量引用 // r1 = 2; // 错误:尝试通过常量引用修改 const_cast<int&>(r1) = 2; // 正确:修改非常量对象 n1 const int& r2 = n2; // 绑定到常量对象的常量引用 // r2 = 2; // 错误:尝试通过常量引用修改 // const_cast<int&>(r2) = 2; // 未定义行为:尝试修改常量对象 n2 [](...){}(n3, n4, x, r2); // 另请参阅:[[maybe_unused]] std::system("g++ -O3 -Wa,-adhln ./main.cpp"); // 在 POSIX 系统上可能生成汇编代码 }
可能的输出:
# 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限定符
最多出现一次 |
| CWG 1799 | C++98 |
mutable
可应用于未声明为
const 的数据成员,但成员类型仍可能为const限定类型 |
不能对const限定类型的
数据成员应用 mutable |
参见
|
C 文档
关于
const
限定符
|
|
|
C 文档
关于
volatile
限定符
|