【问题标题】:Am I right in saying that const_cast followed by modification on a ref-to-const bound to a temporary is okay?我是否正确地说 const_cast 然后修改绑定到临时的 ref-to-const 可以吗?
【发布时间】:2012-01-04 21:54:06
【问题描述】:

我想检查一下我对此事的理解和结论。


在 IRC 上,有人问:

const_cast 是否可以接受绑定到临时对象的 const 引用?

翻译:他有一个 ref-to-const 绑定到一个临时的,他想抛弃它的 const-ness 来修改它。

我的回答是我之前曾问过a similar question,其中的共识似乎是临时人员本身并不是const,因此您可以摆脱const-您所拥有的参考给他们,并通过结果修改他们。而且,只要原来的 ref-to-const 仍然存在,就不会影响临时对象的生命周期。

即:

int main()
{
   const int& x = int(3);

   int& y = const_cast<int&>(x);
   y = 4;

   cout << x;
}
// Output: 4
// ^ Legal and safe

我说的对吗?


(当然,这样的代码是否真正可取完全是另一回事!)

【问题讨论】:

  • @Mark:哦,文字可能是一个糟糕的选择。我现在已经把它变成了这个问题的非文字。
  • 我正要说该代码中没有临时代码。幸运的是,我在做之前刷新了页面:)
  • afaik(但我在 stadnard 中找不到它)它说“当它被声明为 const 时,你可能不会放弃 constness”......所以问题是,是文字声明为常量?我不这么认为,因为它会产生一个临时对象,所以它被声明为 const 吗?我不知道,但我的胆量说:不
  • @MarkB:虽然2.14.2 实际上并没有像2.14.5/12 对字符串文字那样禁止或取消定义修改整数文字。好奇!
  • 相关,但不重复:stackoverflow.com/questions/3484233/…

标签: c++ reference constants const-cast temporary-objects


【解决方案1】:

没有。

首先,据我所知,它是否是文字是 无关的。非类类型的右值始终具有非 cv 限定 types(第 3.10/9 节),然而,在第 8.5.3 节(引用的初始化)中,我们 有:

对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化,如下所示:

[...]

--

否则,将使用非引用复制初始化 (8.5) 的规则从初始化表达式创建和初始化“cv1 T1”类型的临时变量。然后将引用绑定到临时文件。如果 T1 与 T2 引用相关,则 cv1 必须与 cv-qualification 相同或更高 比,cv2;否则,程序是非良构的。

(前面的所有观点都涉及左值或类类型。)

在我们的例子中,我们有:

int const& x = ...;

所以 cv1 T1int const,我们创建的临时对象有类型 int const。这是一个顶级常量(在对象上),所以任何尝试 修改它是未定义的行为。

至少,这是我的解释。我希望标准对此更加清晰。

【讨论】:

  • 这不是让 3.10/9 和 8.5.3 互相矛盾吗?
  • @TomalakGeret'kal 不是真的。 §3.10/9 讨论了右值和左值。像 3int(3) 这样的表达式是一个右值。 §8.5.3 讨论如何初始化引用;引用永远不是右值,并且必须引用一个对象。所以§8.5.3 说编译器创建一个临时对象,用右值初始化,并将引用绑定到那个。你没有得到对整数文字的引用,因为这样的东西不存在;你得到一个对一个不可见的、未命名的对象的引用,它具有引用的类型(而不是初始化器的类型)。
  • 我在@James 的评论中发现的最有趣的事情是:你得到一个对其他不可见的、未命名对象的引用,它具有引用的类型(而不是初始化器)。惊人的。在此之后,一切都非常简单明了!
  • @Nawaz 在阅读标准时,临时对象的类型仍然是int const。返回值是非类类型的右值(因此忽略 cv 限定符 --- 没有 int const f())。所以我们一直到第 8.5.3 节的最后一点(在别处引用)。这里的要点是非类类型与类类型的处理方式不同,对于非类类型,临时具有引用的类型(“cv1 T1”)。 (对于类类型,右值表达式的 const-ness 很重要。)
  • @DonalFellows 在原始的 Fortran 中,是的。像 f(3) 这样的东西实际上可以调用值为 4 的 f。“标准”Fortran 要求生成的代码每次都重新初始化参数,因此 f(3) 确实总是用 3 调用 f
【解决方案2】:

答案取决于临时的创建方式和引用的初始化方式。

如果您自己明确地将临时对象创建为非 const 类型的对象,并且这种情况保证 const-reference 专门附加到您创建的临时对象,那么您可以放心地抛弃引用的 const 性并修改对象。

另一方面,如果临时变量是由编译器为您隐式创建的,那么临时变量本身就是 const。在这种情况下,修改该临时文件会导致 UB。

不幸的是,C++ 语言标准本身似乎并不能保证必须采用第一种初始化方法的任何情况。在任何情况下,编译器都可以引入原始临时文件的额外临时副本。新的临时变量将是 const (如上所述),因此不可修改。是否发生这种情况由实现定义,如 8.5.3/5 中所述。

因此,一般情况下答案是否定的,而特定于实现的答案可能不同。

【讨论】:

  • 我在 8.5.3/5 中找不到任何表明 RHS 上的任何临时文件不会符合 James 引用的条款。您能否证明当我“显式创建临时 [我自己]”时情况有所不同?
  • @Tomalak Geret'kal: 好吧,对于T 的类类型,如果我只使用const T&amp; r = T(),我有机会将参考直接附加到我的T()(说8.5.3/5),这不是 const。在这种情况下,丢弃常量并修改对象是完全合法的。
  • 8.5.3/5 很大,正如我所说,我找不到您正在使用的演绎步骤。
  • @Tomalak Geret'kal:我说的是以“-否则,引用应为非易失性常量类型...”开头的部分。下一个项目符号说“-如果初始化表达式是一个右值......引用以下列方式之一绑定(选择是实现定义的)”。那一点在这里适用,这就是我所指的实现定义的行为。
  • @AndreyT 在执行const T&amp; r = T() 时,从来没有编译器复制过。制作副本没有技术原因,也没有允许制作副本的技术理由。编写“以下列方式之一(选择由实现定义)(...)”部分的人只是不懂 C++。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-02
  • 2015-03-26
  • 2012-05-26
  • 1970-01-01
  • 2018-08-14
  • 1970-01-01
  • 2022-11-01
相关资源
最近更新 更多