реинтерпретировать_приведение к указателю на функцию

Вот код, который я написал для экспериментов с reinterpret_cast<T>

#include <iostream>
#include <cstdlib>

using std::cout;
using std::endl;

int foo()
{
    cout << "foo" << endl;
    return 0;
}

void (*bar)();
int main()
{

    bar = reinterpret_cast<void (*)()>(foo); //Convertion a function type to a pointer to function type
    bar(); //displays foo. Is it UB?
}

Во-первых, почему такое reinterpret_cast преобразование разрешено? Я думал, что такое преобразование плохо сформировано.


person St.Antario    schedule 05.08.2014    source источник
comment
C++ позволяет вам выстрелить в ногу многими интересными способами, и если вам повезет, компилятор предупредит вас об этом. Однако, если вы каким-либо образом отключите предупреждения или ошибки, например, используя reinterpret_cast для приведения одного типа к другому (возможно, несовместимому), многие скажут, что вы заслуживаете того, что получаете.   -  person Some programmer dude    schedule 05.08.2014
comment
@JoachimPileborg Но в 5.2.10/1 сказано, что преобразования, которые можно выполнить явно с помощью reinterpret_cast, перечислены ниже. Никакое другое преобразование не может быть выполнено явным образом с помощью reinterpret_cast. В 5.2.10 такого преобразования нет.   -  person St.Antario    schedule 05.08.2014


Ответы (2)


Стандарт (C++11 §5.2.10/6) говорит

Указатель на функцию может быть явно преобразован в указатель на функцию другого типа. Эффект от вызова функции через указатель на тип функции, который не совпадает с типом, используемым в определении функции, не определен. За исключением того, что преобразование prvalue типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 — типы функций) и обратно к его исходному типу дает исходное значение указателя, результат такого преобразования указателя не определен. .

Так что это неопределенное поведение.

person ahruss    schedule 05.08.2014
comment
Но почему я не получаю ошибку компиляции, когда привожу функцию к указателю на функцию? Не могли бы вы предоставить подходящую ссылку на Стандарт? - person St.Antario; 05.08.2014
comment
Как это не понятно? Указатель на функцию может быть явно преобразован в указатель на функцию другого типа. - person ahruss; 05.08.2014
comment
@St.Antario Поскольку вы используете reinterpret_cast, чтобы обмануть компилятор, что тип foo - это то, чем он не должен быть. - person Some programmer dude; 05.08.2014
comment
Тип функции — это не то же самое, что указатель на тип функции. - person St.Antario; 05.08.2014
comment
Ясно. Но я не передаю указатель на функцию другому указателю на функцию. Я привел тип функции к указателю на функцию. - person St.Antario; 05.08.2014
comment
@St.Antario: Это правда .. Однако тип функции распадается до типа указателя функции практически во всех контекстах. Ситуация очень похожа на то, что происходит с массивами. Единственными контекстами, которые предотвращают тип функции decay, являются унарный оператор &, оператор sizeof и некоторые другие. reinterpret_cast не является исключением, что означает, что каждый раз, когда вы указываете имя функции в качестве операнда, оно неявно преобразуется в тип указателя на функцию. т.е. reinterpret_cast<>(foo) точно эквивалентно reinterpret_cast<>(&foo). - person AnT; 05.08.2014
comment
@St.Antario Из §4.3/1: lvalue функционального типа T может быть преобразовано в prvalue типа «указатель на T». Результатом является указатель на функцию. - person Some programmer dude; 05.08.2014

Формальный вызов через указатель, приведенный к другому типу функции, является неопределенным поведением (согласно C++11 §5.2.10/6).

На практике вы отбрасываете результат функции типа int, который будет возвращен в регистре. Итак, самое худшее, что может произойти, когда вы вызываете через приведенный указатель, это то, что вопреки ожиданиям компилятора значение регистра изменилось.

Еще одно практическое соображение: C++ не поддерживает приведение между указателями на функцию и данные, но Posix фактически требует приведения к void* и обратно, чтобы работать нормально. Ограничение C++ предположительно связано с поддержкой машин с гарвардской архитектурой, где инструкции не извлекаются через ту же шину и память, что и обычные данные. Но обратная передача Posix предположительно будет работать и на такой архитектуре, если только адресное пространство данных не будет намного меньше, чем адресное пространство инструкций.

person Cheers and hth. - Alf    schedule 05.08.2014
comment
Для ясности: приведение не является UB; он определен таким образом, что приведение от типа функции A к типу функции B и обратно к A возвращает вам оригинал. Вызов функции после приведения к другому типу — это то, что не определено. - person ahruss; 05.08.2014