В +
выражении +[](){}
есть унарный +
оператор. В [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.