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

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

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

На вопрос был дан ответ, и все казалось в порядке. Затем пришел Johannes Schaub - litb и сделал интересное наблюдение:

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

Поэтому мне интересно: почему работает следующее?

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

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

1 ответ

Решение

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

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

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

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

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

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

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

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

T* operator+(T*);

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

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

Другие вопросы по тегам