Различия сигнатур функций в C ++ 11 - PullRequest
6 голосов
/ 08 апреля 2011

Учитывая лямбды в C ++ 11 со следующим кодом,

template <typename M>
void call(void (*f)(M), M m)
{
  f(m);
}

int main()
{
  call<int>([](int n) { }, 42);          // OK

  int r;
  call<int>([&](int n) { r = n; }, 42);  // KO
}

есть ли разница между лямбдами, которая делает второй несовместимым с аргументом call?

Я использую g ++ 4.6.1.

Дополнительный вопрос: почему нельзя выводить параметр, если я пишу call([](int n) { }, 42);?

Ответы [ 2 ]

15 голосов
/ 08 апреля 2011

Только нефиксированная лямбда может быть неявно преобразована в указатель на функцию.

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

Тип M не может быть выведен из аргументов функции, потому что требуется преобразование для преобразования лямбда-функции в указатель на функцию. Это преобразование запрещает вывод аргументов шаблона. Если бы вы вызывали функцию call с фактической функцией (например, void f(int)), вывод аргумента работал бы просто отлично.

7 голосов
/ 08 апреля 2011

Как уже ответил Джеймс, только необработанные лямбды могут быть преобразованы в указатели функций.Лямбды с состоянием создают объекты-функторы, которые реализуют operator(), и указатели на функции-члены несовместимы с указателями свободных функций.

Когда компилятор обрабатывает: [&](int n){ r = n; }, он генерирует что-то вроде:

class __annonymous_lambda_type {
   int & r;
public:
   __annonymous_lambda_type( int & r ) : r(r) {}
   void operator()( int n ) const {
      r = n; 
   }
} __lambda_instatiation;

Класс необходим для хранения состояния лямбды, в этом случае ссылка на внешний объект, который будет изменен при выполнении лямбды.То, что void operator()(int) не может быть привязано к void (*)(int).

С другой стороны, если лямбда не имеет состояния, она может быть реализована как свободная функция, как в случае []( int n ) { std::cout << "Hi" << n << std::endl ; }

void __annonymous_lambda_function( int n ) {
   std::cout << "Hi " << n << std::endl;
}

Поскольку лямбда вообще не нуждается в каком-либо состоянии, и поэтому она может быть сохранена как простая функция.

...