Положительная лямбда: '+ [] {}' - Какое волшебство?

В вопросе Переопределение lambdas не разрешено в С++ 11, почему?, была предоставлена ​​небольшая программа, которая не компилируется:

int main() {
    auto test = []{};
    test = []{};
}

На вопрос был дан ответ, и все было хорошо. Затем пришел Йоханнес Шауб и сделал интересное наблюдение:

Если вы ставите + перед первой лямбдой, она волшебным образом начинает работать.

Итак, мне любопытно: Почему работает следующая работа?

int main() {
    auto test = +[]{}; // Note the unary operator + before the lambda
    test = []{};
}

Он компилируется с помощью GCC 4.7+ и Clang 3.2+. Соответствует ли стандарт кода?

Ответ 1

Да, код соответствует стандарту. + запускает преобразование в простой старый указатель функции для лямбда.

Что происходит:

Компилятор видит первую лямбду ([]{}) и генерирует объект замыкания согласно п. 5.1.1. Поскольку лямбда является не захватывающей лямбда, применяется следующее:

5.1.2 Лямбда-выражения [expr.prim.lambda]

6 Тип замыкания для лямбда-выражения без лямбда-захвата имеет общедоступную не виртуальную неявную функцию преобразования const для указателя на функцию, имеющую тот же параметр и возвращаемые типы, что и замыкание type вызывает вызов функции. Значение, возвращаемое этой функцией преобразования, должно быть адресом функции, которая при вызове имеет тот же эффект, что и при вызове оператора вызова функции закрытия.

Это важно, так как унарный оператор + имеет набор встроенных перегрузок, особенно этот:

13.6 Встроенные операторы [over.built]

8 Для каждого типа T существуют кандидатные операторные функции вида

    T* operator+(T*);

И при этом совершенно ясно, что происходит: Когда оператор + применяется к объекту замыкания, набор перегруженных встроенных кандидатов содержит указатель преобразования-на-any-pointer, а тип закрытия содержит ровно один кандидат: Преобразование в указатель функции лямбда.

Таким образом, тип test в auto test = +[]{}; выводится на void(*)(). Теперь вторая строка проста: для второго объекта лямбда/замыкания назначение указателю функции запускает то же преобразование, что и в первой строке. Несмотря на то, что вторая лямбда имеет другой тип закрытия, результирующий функциональный указатель, конечно, совместим и может быть назначен.