Преобразование между типами указателей на функции - PullRequest
0 голосов
/ 23 января 2019

У меня есть программа, которая должна хранить функции как void (*) (void*).Создание функций с этой сигнатурой приводит к дублированию кода (обычно первая строка - приведение указателя void к правильному типу) и снижает безопасность типов (указатель void может быть приведен к чему угодно, например к неправильному типу), поэтому мне было интересноесли я могу взять функцию типа void (*)(T*), а затем преобразовать ее в void(*)(void*) и вызвать ее так, следующим образом:

#include <iostream>
#include <string>

void printer(std::string* string)
{
    std::cout << *string << std::endl;
}

int main() 
{
    //Cast the function to a one that takes a void pointer
    auto func = reinterpret_cast<void(*)(void*)>(&printer);
    //Create a string and call the function with it
    std::string string = "Hello World";
    func(&string);
    return 0;
}

Приведенный выше код компилируется и работает нормально(на ideone ), но мне было интересно, совместимо ли оно со всеми стандартами или неопределяемое поведение и просто правильно работает для моего конкретного примера и ОС

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Это неопределенное поведение.

[expr.call] / 1:

Вызов функции через выражение, тип функции которого отличается от типа функции определения вызываемой функции, приводит к неопределенному поведению.

[expr.reinterpret.cast] / 6:

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

C ++ вообще не позволяет работать очень большому количеству приведений функций, даже если вы думаете, что это безопасно, например, изменение const деталей. Когда вам нужна такая вещь, используйте явную функцию-оболочку. Лямбда без захвата может быть одним из простых способов написать ее, не называя ее; или вы можете определить общий шаблон, чтобы обернуть другие функции так, как вам нужно.

[Странно, C ++ 17 допускает неявное преобразование из указателя на функцию, не вызывающую выброса, в указатель на функцию, которая может быть выбрасывающей, даже несмотря на то, что C ++ 17 делает эти различные типы функций - но тогда у меня нет формулировки, которую я могу см. высказывание, что вам действительно разрешено вызывать функцию через этот преобразованный указатель.]

0 голосов
/ 23 января 2019

Поведение при этом не определено.Стандарт (черновик) гласит:

[expr.reinterpret.cast] Указатель на функцию может быть явно преобразован в указатель на функцию другого типа. [Примечание: эффект вызова функции через указатель на тип функции (9.2.3.5), который отличается от типа, используемого в определении функции, не определен.- примечание конца] За исключением того, что преобразование значения типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами функций) и обратно в его исходный тип дает исходное значение указателя, результато таком преобразовании указателя не уточняется.[Примечание: Смотрите также 7.3.11 для более подробной информации о преобразованиях указателя.- примечание конца]


Вы можете использовать лямбду:

void(*func)(void*) = [](void *string) {
    printer(static_cast<std::string*>(string));
};
...