Положительная лямбда: '+[]{}' - Что это за колдовство?
В вопросе переполнения стека Переопределение лямбд не допускается в 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 присваивание указателю на функцию запускает то же преобразование, что и в первой строке. Хотя вторая лямбда имеет другой тип замыкания, результирующий указатель на функцию, конечно, совместим и может быть назначен.