使用const_cast进行未定义行为的非平凡示例

时间:2016-06-08 16:54:57

标签: c++ undefined-behavior

根据我的理解,以下代码是根据c ++标准的未定义行为(特别是第7.1.5.1.4节[dcl.type.cv] / 4)。

#include <iostream>

struct F;
F* g;

struct F {
    F() : val(5)
    {
        g = this;
    }
    int val;
};


const F f;

int main() {
    g->val = 8;
    std::cout << f.val << std::endl;
}

然而,这会打印'8',我已尝试过每个编译器和优化设置。

问题:是否有一个示例会在这种类型的“隐式const_cast”中出现意外结果?

我希望能有像

的结果一样壮观的东西
#include <iostream>
int main() {
    for (int i = 0; i <=4; ++i)
        std::cout << i * 1000000000 << std::endl;
}

on,例如,gcc 4.8.5 with -O2

编辑:标准

中的相关部分
  

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

回复表示重复的评论;它不是重复的,因为我要求一个“意外”结果发生的例子。

2 个答案:

答案 0 :(得分:7)

不那么壮观:

f.h(省略了警卫):

struct F;
extern F* g;

struct F {
    F() : val(5)
    {
        g = this;
    }
    int val;
};

extern const F f;
void h();

TU1:

#include "f.h"
// definitions
F* g;
const F f;
void h() {}    

TU2:

#include "f.h"
#include <iostream>
int main() {
    h(); // ensure that globals have been initialized
    int val = f.val;
    g->val = 8;
    std::cout << (f.val == val) << '\n';
}

使用1进行编译时打印g++ -O2,使用0进行编译时打印-O0

答案 1 :(得分:0)

&#34; undefined&#34;的主要案例行为通常是,如果有人看到const,他们会认为它没有改变。因此,const_cast故意做一些许多库和程序要么不希望完成的事情,要么将其视为显式的未定义行为。重要的是要记住并非所有未定义的行为都来自标准,即使这是该术语的典型用法。

也就是说,我能够在标准库中找到一个可以应用这种思想的地方来做一些我认为更狭隘地被认为是未定义的行为的东西:使用&#34生成std::map重复键& #34;:

#include "iostream"
#include "map"

int main( )
{
    std::map< int, int > aMap;

    aMap[ 10 ] = 1;
    aMap[ 20 ] = 2;

    *const_cast< int* >( &aMap.find( 10 )->first ) = 20;

    std::cout << "Iteration:" << std::endl;
    for( std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i )
        std::cout << i->first << " : " << i->second << std::endl;

    std::cout << std::endl << "Subscript Access:" << std::endl;
    std::cout << "aMap[ 10 ]" << " : " << aMap[ 10 ] << std::endl;
    std::cout << "aMap[ 20 ]" << " : " << aMap[ 20 ] << std::endl;

    std::cout << std::endl << "Iteration:" << std::endl;
    for( std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i )
        std::cout << i->first << " : " << i->second << std::endl;
}

输出结果为:

Iteration:
20 : 1
20 : 2

Subscript Access:
aMap[ 10 ] : 0
aMap[ 20 ] : 1

Iteration:
10 : 0
20 : 1
20 : 2

使用g++.exe (Rev5, Built by MSYS2 project) 5.3.0构建。

显然,访问密钥与存储对中的密钥值之间存在不匹配。除了通过迭代之外,似乎无法访问20:2对。

我的猜测是这种情况正在发生,因为map是作为树实现的。更改值会将其保留在最初的位置(10将去的地方),因此它不会覆盖其他20键。同时,添加实际的10并不会覆盖旧10,因为在检查键值时,它实际上并不相同

我现在没有标准可以查看,但我认为这会在几个级别上违反map的定义。

它可能也会导致更糟糕的行为,但是使用我的编译器/操作系统组合,我无法让它做任何更极端的事情,比如崩溃。