$5.2.11/7 - "[注意:根据对象的类型,通过指针、左值或指向数据成员的指针的写操作由 const_cast 产生,该 const_cast 丢弃了 const-qualifier68)可能会产生未定义的行为 (7.1. 5.1).]"
本节 (C++03) 的措辞令我惊讶。令人惊讶的是两件事。
a) 首先,使用“may”。为什么是“可能”?标准中的其他地方对未定义的行为非常明确
b)为什么放弃最初的 const 对象的 const 性不是立即“未定义的行为”。为什么触发UB需要写入?
$5.2.11/7 - "[注意:根据对象的类型,通过指针、左值或指向数据成员的指针的写操作由 const_cast 产生,该 const_cast 丢弃了 const-qualifier68)可能会产生未定义的行为 (7.1. 5.1).]"
本节 (C++03) 的措辞令我惊讶。令人惊讶的是两件事。
a) 首先,使用“may”。为什么是“可能”?标准中的其他地方对未定义的行为非常明确
b)为什么放弃最初的 const 对象的 const 性不是立即“未定义的行为”。为什么触发UB需要写入?
a) 首先,使用“may”。为什么是“可能”?标准中的其他地方对未定义的行为非常明确
不要太深入地研究“可能”这个词的用法。关键是,在这种情况下抛弃 constness 会导致未定义的行为。
C++ 标准经常使用“may”或“might”,例如:
1.3.12:当本国际标准省略对任何明确的行为定义的描述时,也可能出现未定义的行为。
强调我的。基本上,该标准使用“允许”中的“可能”一词。
b)为什么放弃最初的 const 对象的 const 性不是立即“未定义的行为”。为什么触发UB需要写入?
写入会触发 UB,因为 const 对象可能会存储在某些平台上的只读内存中。
我的理解是,如果所讨论的对象基本上是 const 对象而不是 const 指针或对最初不是 const 的对象的引用,它只会是 UB。
这个想法是,本质上是 const 的数据可以加载到内存的只读部分中,而写入该部分是行不通的。但是,如果所讨论的对象基本上是可变的,则可以保证正常工作。
前任:
const int x = 4;
const int *y = x;
*const_cast<int*>(x) = 3; // UB - the pointed-to object may
// be in read-only memory or whatever.
int a = 7;
const int *b = a;
*const_cast<int*>(b) = 6; // Not UB - the pointed-to object is
// fundamentally mutable.
对于下面的评论,因为代码在评论中看起来很糟糕:
在标准的 §7.1.5.1/4 中,给出的示例是:
int i = 2;
const int * cip; // pointer to const int
cip = &i; // OK: cv-qualified access path to unqualified
...
int* ip;
ip = const_cast <int *>( cip ); // cast needed to convert const int* to int*
*ip = 4; // defined: *ip points to i, a non-const object
所以这是特别允许的。
对于您的第一个问题,如果某些事情可能会产生未定义的行为,那么这并不会使它变得不那么未定义。
对于第二部分,我想这是出于互操作性的原因。例如,C 没有(或在 C99 之前没有)有const关键字,所以如果你想将 const 对象传递给 C 函数,就必须抛弃 const 。因此,C++ 标准规定只要不执行写入,就允许这样做。如果 C 函数是只读的,则可以安全地丢弃常量。
即使在 C++ 中,不一致或不完整的 const 正确性也很常见。所以我们偶尔会遇到我们必须抛弃 const-ness 以便将 const 对象传递给不修改其参数但通过非 const 获取它的函数的情况。
我相信这是因为一个const对象可以存储到只读内存中。因此,写入它可能会产生许多不同的影响:程序崩溃、分段错误或无效。