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



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

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;
}

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

CCD_ 1是CCD_ 2的常量。根据我对const语义的理解,这意味着以任何方式修改它都是UB。然而,我们可以在构造函数中对其进行非常数引用,然后通过它进行修改,可以在const成员函数中进行,也可以直接进行。

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

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

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

以下代码是否有未定义的行为,或者这很好?

它有UB。标准说:

[dcl.type.cv]

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

x是常量,您可以修改它的不可变成员。

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

确实如此。两者都是UB,原因相同。

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

footgun的触发器是对象在其构造函数内时临时为非常量的问题。因此,指向非常数"的指针和引用;这个";并且无论对象是否为常量,它的子对象在构造函数中都是可用的。因此,我们可以得出这样的结论:存储这些指针/引用以供以后使用是不明智的。

将指针和引用存储为引用"的成员;这个";也是出于其他几个原因。如果您要直接通过引用的成员的名称访问该成员,则它们需要存储,否则这是不必要的。此外,您会发现类的复制语义很可能不是您所想的那样。

如果要指向多个备选方案中的一个成员,请使用成员指针,而不是对象指针/引用(在这种情况下,无法避免使用存储(。这解决了复制和意外的常量冲突。

最新更新