2016-10-26 8 views
7

Скажем, у меня есть следующие типы:переинтерпретировать литье указатели на типы стандартных макетов с общими префиксами

struct Common { int a, b, c; }; 
struct Full { int a, b, c; uint64_t x, y, z; }; 

Common и Full типы стандартных макетов, где Common является префиксом Full. Так что, если я ставлю как в союз:

union U { 
    Common c; 
    Full f; 
}; 

Я бы позволил прочитать c даже если f был активным членом в [class.mem]/23.

Теперь вопрос в том, есть ли способ для меня, учитывая Full const*, чтобы получить Common const* в не-UB-способе?

void foo(Full const* f) { 
    Common c1; 
    memcpy(&c1, f, sizeof(c1)); // this obviously works, but I don't want 
           // to be copying all this stuff 

    auto c2 = reinterpret_cast<Common const*>(f); // is this ok? 
     // c2 and f are pointer-interconvertible iff f comes from a U 
     // but why does that U actually need to exist? 

    auto u = reinterpret_cast<U const*>(f); // ok per basic.lval/8.6?? 
    auto c3 = &u->c;      // ok per class.mem/23?? 
} 
+0

«pointer-interconvertible» - это свойство объектов, а не типов. Если нет объединения, то результат 'reinterpret_cast' по-прежнему указывает на' * f'. –

+0

@ T.C. Да, но разве не странно требовать нерелевантного объекта неуместного типа? – Barry

+0

Не совсем - это исключение для системы типов, которая существует только для совместимости с C. Напомним, что P0137R0 вне закона вне закона вне закона. –

ответ

0

Короткий ответ: переосмыслить листинг адреса объекта в другом классе и доступ через указатель - это неопределенное поведение.

Я думаю, что причина заключается в том, чтобы включить более простой анализ псевдонимов компилятором. Если компилятор может предположить, что объект типа X не может быть псевдонимом объекта несвязанного типа Y, даже если он совместим с макетом, тогда он может выполнять оптимизации, которые он не мог бы сделать иначе.

struct X 
{ 
    int a; 
    int b; 
}; 

struct Y 
{ 
    int c; 
    int d; 
}; 

void updateXY(X *x, Y* y) 
{ 
    x->a = y->c; 
    x->b = y->c; // if *x and *y could alias, y->c would have to be reloaded 
} 

void updateXX(X *x, X* xx) 
{ 
    x->a = xx->a; 
    x->b = xx->a; // the compiler must reload xx->a because x and xx may alias 
} 

Фактически gcc и clang делают оптимизацию updateXY больше, чем updateXX, поскольку он рассматривает псевдонимы.

updateXY(X*, Y*): 
    mov eax, DWORD PTR [rsi] // cache y->c 
    mov DWORD PTR [rdi], eax 
    mov DWORD PTR [rdi+4], eax 
    ret 
updateXX(X*, X*): 
    mov eax, DWORD PTR [rsi] 
    mov DWORD PTR [rdi], eax 
    mov eax, DWORD PTR [rsi] // reload y->c 
    mov DWORD PTR [rdi+4], eax 
    ret 

Это не похоже, чтобы сделать разницу для анализа псевдонимов видим ли компилятор союз между X и Y, т.е. оптимизация осуществляется независимо. Таким образом, на практике код, который прошел вокруг указателей на неактивные члены профсоюза, все равно может сломаться. Для вашего удобства прохождение const указателей это должно быть неактуально.