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