本文为简单读后感/翻译/粗浅理解

cv-qualifier实际上是 cv (const and volatile) type qualifiers的缩写。

1 什么变量是const 的?

如果一个变量:

  1. const修饰;或
  2. 是一个const对象的non-mutable成员

那么它就是const的。
一个const的变量在代码里不能直接的修改它,否则会编译错误。但是我们依然可以隐式地修改它,比如用指针去修改它之类的。这样的操作是未定义的,因为编译器会对const做一系列优化,修改了它之后谁也不知道会发生什么事情。

2 什么变量是volatile的?

如果一个变量:

  1. volatile修饰;或
  2. 是一个volatile对象的成员;或
  3. 是一个const-volatile对象的mutable成员

那么它就是volatile的。
每次对它的访问(包括读、写、调用成员函数等)都会被视为一个visable side-effect,编译器会保证它和发生在它之前或之后的visable side-effect依然保持原来的顺序。也就是说编译器不会随便对它做优化,因为人告诉编译器这个变量可能会发生编译器无法预知的变化。
比如在下面这段代码里,显然变量a是不会变化的,这样的话编译器(gcc 11.2 -O2)就会把while循环给优化成一个条件永真的死循环:
![SYPCHAEM6[O(17NAHY@W~6.png](https://cdn.nlark.com/yuque/0/2022/png/762504/1659929686241-a5af8ea3-3bcd-4644-80c6-e91fe27c962d.png#clientId=u499af7f2-25f7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=534&id=u3cc6c36e&margin=%5Bobject%20Object%5D&name=SYPCHAEM6%5BO%2817NAHY%40W~6.png&originHeight=935&originWidth=2572&originalType=binary&ratio=1&rotation=0&showTitle=false&size=192362&status=done&style=none&taskId=u3c972b73-b137-49e8-8a3e-9bccff054cf&title=&width=1469.7142857142858)

然而如果我们声明变量avolatile的,那么每次while循环的时候都会去访问一下a然后和 10 做一下比较:
%RKH@T4Q9F~7%ZX`6QZA_@D.png

这个看起来和 memory_order 有点类似,但并不是一种东西。volatile只是对一个thread里的操作进行约束,多线程之间并没有保证。

3 什么变量是const volatile的?

一般来说很难见到const volatile的变量,它们是:

an object whose type is const-volatile-qualified, a non-mutable subobject of a const volatile object, a const subobject of a volatile object, or a non-mutable volatile subobject of a const object.

  1. 它既有`volatile`的性质,也有`const`的性质。也就是说我们不让程序修改这个值,但是我们又告诉编译器有可能别的地方会有人修改这个值。那么它有什么用呢?<br />下面这个回答告诉我们:在嵌入式里,他们经常见到这样的写法。这是因为很多时候会遇到这样一种变量,它们和硬件端口绑定,所以随时可能会被硬件修改;然而我们的程序不应该去修改这个值。这种情况显然符合`const volatile`的场景。比如你想读取`ALU``ready`位以确定当前的运算是不是结束了,那么这个值显然是当前程序不应该修改也不能修改的,而且它还随时可以被别的人(硬件)修改。

4 什么是mutable修饰符?

有些时候我们希望一个函数被声明为const的,但是又不得不在这个函数里面对一个变量进行修改,此时我们就可以声明这个变量为mutable的。
当然,我们很自然地会产生一个问题,既然你想修改变量,那就不要把自己声明成const就好了。这的确是一种解决方案,但是有可能整个函数都非常的const仅仅是那么一个变量破坏了这个规矩,或者是这个函数从语义上来说就非常const,仅仅因为那个小变量就放弃了可能的编译器优化,不是非常值当。
这里有一个 best practice 叫做:M & M rule,它是指:我们应该把类里的 mutex 成员声明为mutable。这里是想解决这样一个问题:我们很多时候希望这个类是线程安全的,所以当我们读取一个成员变量的时候调用getXXX()函数需要在函数里加锁。这里可能会用到类里的那个 mutex 成员变量,而且加锁会改变这个 mutex,显然这个函数就不能是const的;然而我们知道一个getXXX函数(类似于一个 getter )从道理上来说就应该是const的。这里就可以把这个 mutex 变量声明成为一个mutable变量。

5 转化优先级

不知道应该怎么翻译这一段内容,直接复制上来了:
There is partial ordering of cv-qualifiers by the order of increasing restrictions. The type can be said more or less cv-qualified than:

  • unqualified < const
  • unqualified < volatile
  • unqualified < const volatile
  • const < const volatile
  • volatile < const volatile

References and pointers to cv-qualified types may be implicitly converted to references and pointers to more cv-qualified types.
它告诉我们一个没有 cv 修饰符的变量可以被转化为 const、volatile 或 const volatile 的引用。这也是我们写代码的时候经常这样写的依据:

  1. void foo (const int &a) {
  2. std::cout << a << std::endl;
  3. }
  4. int a = 100;
  5. foo(a);
  6. foo(10);