docs: address community issue feedback (#5 #338 #142 #246 #136 #303)

- const README: rewrite the space-saving claim per issue #5 follow-ups
  (constant-expression const vs #define immediates; which const qualify
  as constant expressions)
- const README: fix the class-section diagnosis per issue #338 (overload
  resolution picks the const overload; the restriction only surfaces
  once that overload is removed)
- sizeof README: virtual-inheritance result is compiler-dependent
  (GCC merges vbptr+vfptr -> 16, MSVC keeps them separate -> 32) #246
- static README: new section on internal linkage / access restriction
  with anonymous-namespace comparison #142
- 10-day practice: full problem list table + fixed day links #136
- new design_pattern/singleton/singleton_default.hpp: reusable
  thread-safe CRTP singleton template, compile+concurrency tested #303
This commit is contained in:
light-city
2026-10-13 19:23:54 +08:00
parent cfc9af1fcb
commit dfe1c933b3
159 changed files with 20461 additions and 182 deletions

View File

@@ -0,0 +1,75 @@
> 来自 [C++那些事网站](https://light-city.github.io/stories_things/cpp-things/cpp-mutable-keyword/) 的文章,由站点同步脚本导出。
# C++ mutable 关键字
### 深入探讨 C++ 中的 `mutable` 关键字
在 C++ 中,`mutable` 关键字经常被认为是“穿透”常量(`const`)限制的工具。在某些情况下,虽然我们希望对一个对象施加常量保护,但还是需要对该对象的某些数据成员进行修改。本文将详细探讨 `mutable` 的使用场景和背后的设计哲学,并讨论其如何在 C++ 中解决特定的编程需求。
#### 1. `mutable` 关键字的基本用途
我们知道,当一个成员函数被 `const` 修饰时,该成员函数保证不会修改对象的状态。然而,在实际编程中,有时我们希望修改一个数据成员,但仍然希望将成员函数标记为 `const`。这就是 `mutable` 关键字的作用:
```cpp
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const {
// 在 const 成员函数中修改 mutable 成员
done_ = true;
}
};
```
在上面的例子中,`doSomething()` 被声明为 `const`,表明它不会改变对象的状态。然而,`done_` 被标记为 `mutable`,这意味着即使在 `const` 方法中,我们仍然可以修改 `done_`。这种设计使得我们可以在保持函数接口语义不变的情况下,管理某些需要修改的数据。
#### 2. 逻辑常量性 vs. 位常量性
C++ 中的常量性可以分为两类:**逻辑常量性** 和 **位常量性**。逻辑常量性指的是对象的状态在外部看来没有发生变化,而位常量性指的是对象在内存中的位数据保持不变。`mutable` 关键字允许我们定义逻辑常量性,即即使对象的某些内部数据变化了,但在逻辑上对象的状态并未发生可见变化。
比如在一个线程安全的类中,我们可能会使用 `boost::mutex` 来保护数据访问。为了在 `const` 方法中获取锁,我们可以将 `mutex` 声明为 `mutable`:
```cpp
class ThreadSafeClass {
private:
mutable std::mutex mtx_;
int data_;
public:
int getData() const {
std::lock_guard<std::mutex> lock(mtx_);
return data_;
}
};
```
在上述例子中,虽然 `getData` 方法被声明为 `const`,但我们需要锁来保护数据访问的线程安全性。因此,我们将 `mtx_` 声明为 `mutable`,以允许在 `const` 方法中对其进行修改。这种情况下,我们说类的逻辑状态没有改变,只是为确保线程安全而进行的修改。
#### 3. 在 Lambda 表达式中的 `mutable`
从 C++11 开始,`mutable` 也可以用于 lambda 表达式,以允许修改按值捕获的变量。默认情况下,lambda 中按值捕获的变量是不可变的。如果需要在 lambda 中修改捕获的变量,可以使用 `mutable`:
```cpp
int x = 0;
auto f1 = [=]() mutable { x = 42; }; // OK: 可以修改捕获的 x
auto f2 = [=]() { x = 42; }; // Error: 按值捕获的变量不能被修改
```
这种情况下,`mutable` 允许我们在函数体内修改捕获的变量,而不会影响到外部变量的值。
#### 4. 避免使用 `const_cast`
没有 `mutable` 关键字的情况下,我们可能需要使用 `const_cast` 来处理这些特殊情况。`const_cast` 可以去掉对象的常量性,但它也有缺点:`const_cast` 可以完全移除常量性保护,让我们对对象进行任意的修改和方法调用,这会导致代码的可读性和安全性下降。
相比之下,`mutable` 关键字提供了一种更细粒度的控制方式。通过 `mutable`,我们可以在特定的数据成员上设置“例外”,允许在 `const` 方法中修改,而不必将整个对象的常量性去掉。因此,`mutable` 能够更安全地处理这些特例。
#### 5. 使用 `mutable` 的设计哲学
`mutable` 关键字的设计哲学在于保持接口的**逻辑常量性**。在大多数情况下,我们希望编写 const-correct 的代码,这意味着对象的状态在没有显式修改的情况下不会变化。`mutable` 允许我们在某些内部实现上做出让步,比如缓存、引用计数、调试信息等,而不会打破这种接口设计的一致性。
设计一个接口时,我们应该明确何时以及为什么需要修改一个对象的状态。`mutable` 关键字能让代码在逻辑层面保持不变,同时又提供了修改特定数据成员的能力。这需要设计者深思熟虑,确保 `mutable` 的使用不会破坏对象的常量性概念,只有在确实需要的情况下使用。
### 6.参考资料
> https://stackoverflow.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-a-data-member-to