Приведение лямбда с не пустым типом возврата к указателю на функцию - PullRequest
0 голосов
/ 26 января 2020

Я пытаюсь преобразовать лямбда-функцию в указатель на функцию . Однако приведение не выполняется, когда лямбда имеет тип возврата, отличный от void. Подробности см. Фрагмент кода.

Возможно ли преобразовать fun2 в generi c указатель функции , который я могу сохранить для последующего использования?

Обновление: я намерен разыграть все виды функций в «обобщенный c указатель функции», который можно сохранить в std::map. После использования я приведу его к исходному типу функции.

#include <iostream>

int fun3() { return 1; }

int main(int argc, char *argv[]) {

  typedef void (*voidFunctionType)(void);

  // Case 1: lambda, return type void 
  auto fun1 = []() { std::cout << "hello" << std::endl; };
  // -> compiles
  auto casted_fun1 = (voidFunctionType)fun1;

  // Case 2: lambda, return type int
  auto fun2 = []() { std::cout << "world" << std::endl; return -1;};
  // -> error: invalid cast from type ‘main(int, char**)::<lambda()>’ to type ‘voidFunctionType {aka void (*)()}’
  auto casted_fun2 = (voidFunctionType)fun2;

  // Case 3: free function, return type int -> compiles
  auto casted_fun3 = (voidFunctionType)fun3;

  return 0;
}

1 Ответ

1 голос
/ 26 января 2020

Проблема в том, что вы используете явные приведения в стиле C. Они общеизвестно опасны.

Здесь в этом случае проблема заключается в том, что fun3 (в отличие от fun2) уже распадается на указатель функции типа int(*)().

Затем вы приведите его к void(*)(). Это работает, потому что приведение в стиле C будет пытаться выполнять различные выражения приведения C ++, пока одно из них не сработает. В частности, он также попытается reinterpret_cast.

reinterpret_cast<voidFunctionType>(fun3)

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

Однако , вы не можете вызывать функцию через полученный указатель. В результате ваша программа будет иметь неопределенное поведение . Как вы можете видеть, это приведение очень ограничено и опасно, если вы о нем не знаете.

Не используйте приведение в стиле C, используйте вместо него static_cast<voidFunctionType>(fun3), и вы получите соответствующую компиляцию ошибка времени в обоих случаях.

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


Если вы действительно хотите сохранить произвольную функцию, указатель на вас можно заставить лямбда-приведение работать, сначала преобразовав его в указатель функции, а затем приведя его к типу указателя функции назначения с reinterpret_cast. Я бы по-прежнему не использовал приведения в стиле C, потому что reinterpret_cast по крайней мере прояснит, какой тип приведения вы собираетесь делать:

auto casted_fun2 = reinterpret_cast<voidFunctionType>(+fun2);

Унарный + - распространенный трюк заставить лямбду к преобразованию указателя на функцию. Обратите также внимание на то, что только лямбда-выражения без перехвата могут быть преобразованы в указатели функций.

Как я объяснил выше, вы должны привести указатель обратно к его первоначальному типу перед вызовом, поэтому вам нужно где-то хранить информацию о типе. Я не уверен, как вы собираетесь это сделать, но вам, вероятно, нужно реализовать какую-то расширенную версию std::function, см., Например, этот вопрос , как std::function делает это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...