Могу ли я безопасно привести к функции, возвращающей void? - PullRequest
0 голосов
/ 06 марта 2019

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

Что не очевидно (для меня), так это то, должно ли быть безопасно приводиться к типу указателя на функцию, где тип возвращаемого значения void (и типы аргументов одинаковы).

Есть ли механизм для безопасного приведения, например, от int (*)() до void (*)()?static_cast терпит неудачу здесь.reinterpret_cast работает, но небезопасно.

Есть ли безопасный способ?

std::function<void()>(funcReturningInt), кажется, справляется с задачей, но я не могу смотреть через его реализацию лабиринта впосмотри как.

Ответы [ 4 ]

2 голосов
/ 06 марта 2019

Безопасных приведений нет.

Ответ в соответствии со стандартом c ++ дан другим ответом, здесь я хочу объяснить, почему это так.

Существуют разные способы обработкивозвращаемые значения, C / C ++ не определяет и не ограничивает способы обработки возвращаемых значений.Они определены внутри ABI.

В X86 ABI определено несколько соглашений о вызовах, например cdecl, stdcall или thiscall.Различные соглашения о вызовах обрабатывают возвращаемые значения по-разному, наиболее важным здесь является то, что где хранятся возвращаемые значения.

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

Если возвратзначения хранятся внутри регистров, вызывающий может предположить, что эти регистры не были изменены при вызове функции, возвращающей void, но если это не так, эти регистры могут быть изменены вызываемым пользователем.Разногласия могут также привести к сбою.

Если вы настаиваете на использовании приведения типа, просто убедитесь, что вы выбрали правильное соглашение о вызовах, которое не влияет на возвращаемые значения.

2 голосов
/ 06 марта 2019

В соответствии со стандартом безопасных приведений не существует

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

Надеюсь, это поможет.

0 голосов
/ 06 марта 2019

Для полноты, что std :: function делает это с помощью шаблонного конструктора. Шаблонный конструктор эффективно создает функцию адаптера статической подписи для каждого типа подписи, который запрашивается для хранения, и сохраняет то, что составляет пустой указатель на сохраненную функцию и указатель на функцию адаптера.

Во время вызова он вызывает функцию адаптера с указателем void и возвращает его обратно к исходному типу.

0 голосов
/ 06 марта 2019

Как уже отмечали другие, бросок небезопасен. И std::function не нужен. Если вы хотите передать функцию чему-либо, ожидающему указатель на функцию void, и игнорировать возвращаемый результат, вы можете легко сделать это с помощью лямбды:

int foo();

auto gimme_gimme_a_function_after_midnight(void (*f)() )
{
    f();
}

auto test()
{
    gimme_gimme_a_function_after_midnight([]() { foo(); });
}

Лямбда просто вызывает функцию и игнорирует возвращаемое значение. А поскольку он не имеет перехвата, он может неявно приводиться к указателю на функцию.

Если функция имеет параметры, которые легко адаптировать.

...