Подтвердить что ты не робот

Нетривиальный пример поведения undefined с const_cast

Следующий код, насколько я понимаю, соответствует undefined по стандарту С++ (в частности, раздел 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;
}

на, например, gcc 4.8.5 с -O2

EDIT: соответствующий раздел из стандартного

7.1.5.1.4: За исключением того, что любой член класса, объявленный mutable (7.1.1), может быть изменен, любая попытка изменить объект const в течение его жизни (3.8) приводит к поведению undefined.

В ответ на комментарий, предлагающий дубликат; это не дубликат, потому что я прошу пример, где происходят "неожиданные" результаты.

4b9b3361

Ответ 1

Не так эффектно:

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.

Ответ 2

Основной случай поведения "undefined" заключается в том, что, как правило, если кто-то видит const, они предполагают, что он не изменяется. Таким образом, const_cast намеренно делает то, что многие библиотеки и программы либо не будут ожидать, либо будут рассматриваться как явное поведение undefined. Важно помнить, что не все поведение undefined происходит только от стандарта, даже если это типичное использование термина.

Тем не менее, я смог найти место в стандартной библиотеке, где такое мышление может быть применено для выполнения того, что, по моему мнению, будет более узко рассматриваться как поведение undefined: создание std::map с "дублирующими ключами":

#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 на нескольких уровнях.

Это может также привести к худшему поведению, но с моим компилятором /OS combo мне не удалось заставить его сделать что-то более экстремальное, например, сбой.