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