通过构造函数绑定引用更改 const 对象的成员是 UB 吗?

Max*_*hof 5 c++ reference constants language-lawyer

简而言之:以下代码是否有未定义的行为,或者这样可以吗?

struct X
{
  X(int b)
    : value(b)
    , ref(value)
  {
  }

  int value;
  int& ref;

  void isThisUB() const
  {
    ref = 1;
  }
};

int main()
{
  const X x(2);
  // Is either of these fine?
  x.isThisUB();
  x.ref = 3;
  return x.value;
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/1TE9a7M4a

X::value是常量x。根据我对const语义的理解,这意味着以任何方式修改它都是UB。然而,我们可以在构造函数中获取对它的非常量引用,然后通过它来修改它,无论是在const成员函数中还是直接修改。

C++(至少 17)标准在[dcl.type.cv]中给出了与 const 相关的 UB 示例,它看起来基本相同,只是它使用const_cast. 注意如何p->x.j = 99表示为 UB。我没有看到使用const_cast上面的代码实现这一点有根本的区别。

那么,上面的代码是UB吗?非常量引用成员/指针真的有这么大吗?

(如果你能想出产生相关问题的搜索关键字,而不仅仅是随机的const东西,我会印象深刻。)

eer*_*ika 4

下面的代码是否有未定义的行为,或者这样可以吗?

它有UB。标准说:

[dcl.type.cv]

除了声明为可变的任何类成员都可以修改之外,任何在其生命周期内修改 const 对象的尝试都会导致未定义的行为

x是 const 并且您修改其非可变成员。

我不认为使用 const_cast 实现这一点与我上面的代码有根本区别。

的确。出于同样的原因,两者都是 UB。

非常量引用成员/指针真的有这么大吗?

footgun 的触发因素是对象在其构造函数内时暂时为非常量。因此,对非常量“this”及其子对象的指针和引用在构造函数中很容易获得,无论该对象是否是 const。因此我们可以得出结论,存储这些指针/引用以供以后使用是不明智的。

由于其他几个原因,将指针和引用存储为引用“this”的成员也很危险。它们需要存储,如果您要直接通过其名称访问引用的成员,则不需要这些存储。此外,您会发现该类的复制语义可能不是您想要的。

如果您想指向多个替代方案中的一个成员,请使用成员指针,而不是对象指针/引用(在这种情况下无法避免使用存储)。这解决了复制和意外的 const 违规问题。