В +выражении +[](){}есть унарный +оператор. В [expr.unary.op] / 7 это определено следующим образом:
Операнд унарного +оператора должен иметь арифметический, перечисление без области видимости или тип указателя, а результатом является значение аргумента.
Лямбда не имеет арифметического типа и т. Д., Но может быть преобразована:
[expr.prim.lambda] / 3
Тип лямбда-выражения [...] является уникальным, безымянным типом класса без объединения, называемым типом замыкания , свойства которого описаны ниже.
[expr.prim.lambda] / 6
Тип закрытия для лямбда-выражения , без лямбда-захвата имеет publicне- virtualне- explicit constфункцию преобразования в указатель на функцию , имеющую те же значения параметров и возвращаемых типов в качестве оператора вызова функция замыкающим в. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и вызов оператора вызова функции закрывающего типа.
Следовательно, унарный принудительно +приводит к преобразованию в тип указателя функции, который предназначен для этой лямбды void (*)(). Следовательно, тип выражения +[](){}- это тип указателя на функцию void (*)().
Вторая перегрузка void foo(void (*f)())становится точным совпадением в рейтинге для разрешения перегрузки и поэтому выбирается однозначно (поскольку первая перегрузка НЕ является точным совпадением).
Лямбда [](){}может быть преобразован в std::function<void()>через без явного шаблона CTOR из std::function, который принимает любой тип , который удовлетворяет Callableи CopyConstructibleтребованиям.
Лямбда также может быть преобразована void (*)()в функцию преобразования типа закрытия (см. Выше).
Оба являются пользовательскими последовательностями преобразования и имеют одинаковый ранг. Вот почему в первом примере разрешение перегрузки не удается из-за неоднозначности.
По словам Кассио Нери, подкрепленного аргументом Даниэля Крюглера, этот унарный +трюк должен иметь конкретное поведение, то есть на него можно положиться (см. Обсуждение в комментариях).
Тем не менее, я бы рекомендовал использовать явное приведение к типу указателя функции, если вы хотите избежать двусмысленности: вам не нужно спрашивать у SO, что он делает и почему он работает;)
std::bindкstd::functionобъекту, который может вызываться аналогично функции lvalue.