const_cast的未定义行为

Mun*_*nro 9 c++ const-cast undefined-behavior

我希望有人能够准确地澄清C++中未定义行为的含义.给定以下类定义:

class Foo
{
public:
    explicit Foo(int Value): m_Int(Value) { }
    void SetValue(int Value) { m_Int = Value; }

private:
    Foo(const Foo& rhs);
    const Foo& operator=(const Foo& rhs);

private:
    int m_Int;
};
Run Code Online (Sandbox Code Playgroud)

如果我已经正确理解了以下代码中的引用和指针的两个const_casts将删除Foo类型的原始对象的常量,但是通过指针或引用修改此对象的任何尝试都将导致未定义的行为.

int main()
{
    const Foo MyConstFoo(0);
    Foo& rFoo = const_cast<Foo&>(MyConstFoo);
    Foo* pFoo = const_cast<Foo*>(&MyConstFoo);

    //MyConstFoo.SetValue(1);   //Error as MyConstFoo is const
    rFoo.SetValue(2);           //Undefined behaviour
    pFoo->SetValue(3);          //Undefined behaviour

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

让我感到困惑的是为什么这似乎有效并将修改原始的const对象,但是甚至没有提示我发出警告通知我这种行为是未定义的.从广义上讲,我知道const_casts是不受欢迎的,但是我可以想象一种情况是缺乏对C风格的强制转换可以导致const_cast的意识,而不会被注意到,例如:

Foo& rAnotherFoo = (Foo&)MyConstFoo;
Foo* pAnotherFoo = (Foo*)&MyConstFoo;

rAnotherFoo->SetValue(4);
pAnotherFoo->SetValue(5);
Run Code Online (Sandbox Code Playgroud)

在什么情况下这种行为会导致致命的运行时错误?是否有一些编译器设置可以设置为警告我这种(可能)危险的行为?

注意:我使用的是MSVC2008.

Lig*_*ica 11

我希望有人能够准确地澄清C++中未定义行为的含义.

从技术上讲,"未定义行为"意味着该语言没有定义用于执行此类操作的语义.

在实践中,这通常意味着" 不要这样做 ;当编译器执行优化时,或者出于其他原因,它可能会中断".

让我感到困惑的是为什么这似乎有效并将修改原始的const对象,但是甚至没有提示我发出警告通知我这种行为是未定义的.

在这个具体的例子中,尝试修改任何非可变对象可能"似乎工作",或者它可能覆盖不属于该程序或属于某个其他对象的[部分]的内存,因为不可变对象可能在编译时已经过优化,或者可能存在于内存中的某些只读数据段中.

可能导致这些事情发生的因素太复杂而无法列出.考虑解除引用未初始化指针(也是UB)的情况:您正在使用的"对象"将具有一些任意内存地址,该地址取决于指针位置内存中发生的任何值; "价值"可能依赖于之前的程序调用,以前在同一程序中的工作,存储用户提供的输入等.尝试合理化调用未定义行为的可能结果是不可行的,所以,我们通常不会打扰而只是说" 不要这样做 ".

让我感到困惑的是为什么这似乎有效并将修改原始的const对象,但是甚至没有提示我发出警告通知我这种行为是未定义的.

作为进一步的复杂化,编译器不需要诊断(发出警告/错误)未定义行为,因为调用未定义行为的代码与不正确的代码(即明确非法)不同.在许多情况下,编译器甚至无法检测UB,因此编程人员有责任正确编写代码.

类型系统 - 包括const关键字的存在和语义- 提供了基本的保护,防止编写将破坏的代码; 一个C++程序员应该始终意识到颠覆这个系统 - 例如通过黑客攻击const- 是由你自己承担风险,并且通常是一个坏主意.

我可以想象一种情况,即缺乏C风格演员可能导致const_cast制作的意识可能会在没有被注意的情况下发生.

绝对.在警告级别设置得足够高的情况下,理智的编译器可能会选择警告您这一点,但它没有,也可能没有.一般来说,这是为什么C风格的演员表示不满意的一个很好的理由,但它们仍然支持与C的向后兼容性.这只是其中一个不幸的事情.


Nik*_*iou 5

未定义的行为取决于对象的诞生方式,您可以在 00:10:00 左右看到Stephan对其进行解释,但本质上,请遵循以下代码:

void f(int const &arg)
{
    int &danger( const_cast<int&>(arg); 
    danger = 23; // When is this UB?
}
Run Code Online (Sandbox Code Playgroud)

现在调用有两种情况f

int K(1);
f(k); // OK
const int AK(1); 
f(AK); // triggers undefined behaviour
Run Code Online (Sandbox Code Playgroud)

综上所述,K出生是一个非常量,所以在调用 f 时强制转换是可以的,而AK出生是一个const所以... UB 是这样。