Estaba probando un ejemplo presentado en CppCon que usa lambdas . Y para mi sorpresa, el programa no compila en gcc y clang (en C++ 14 o C++ 17), sino que compila en msvc. Esto se puede verificar aquí .
Como referencia, el código de ejemplo es el siguiente:
#include <stdio.h> int g = 10; auto kitten = [=]() { return g+1; }; auto cat = [g=g]() { return g+1; }; int main() { g = 20; printf("%d %d\n", kitten(), cat()); }¿Cuál es el problema aquí (si lo hay) y qué compilador es el correcto?
Tenga en cuenta que el código es un copiado y pegado exacto de su diapositiva de presentación oficial.
Este es un problema con MSVC. clang y g++ son correctos.
De [expr.prim.lambda.capture]/3 (C++17 borrador N4659)
Una expresión lambda cuyo ámbito envolvente más pequeño es un ámbito de bloque (6.3.3) es una expresión lambda local; cualquier otra expresión lambda no tendrá una captura predeterminada o una captura simple en su introductor lambda. El ámbito de alcance de una expresión lambda local es el conjunto de ámbitos envolventes hasta la función envolvente más interna y sus parámetros, inclusive. [ Nota: este ámbito de alcance incluye cualquier expresión lambda intermedia. —nota final]
Dado que = es una capture-default y la lambda no está dentro de un alcance de bloque, el código no es válido.
¿Cuál es el problema aquí (si lo hay)
Preguntemos qué tienen que decir los compiladores que se niegan a compilar:
error: non-local lambda expression cannot have a capture-default
Veamos qué dice la norma:
C++14 borrador N4140
Una expresión lambda cuyo ámbito envolvente más pequeño es un ámbito de bloque ([basic.scope.block]) es una expresión lambda local; cualquier otra expresión lambda no tendrá una captura predeterminada o una captura simple en su introductor lambda. El ámbito de alcance de una expresión lambda local es el conjunto de ámbitos envolventes hasta la función envolvente más interna y sus parámetros, inclusive. [ Nota: este ámbito de alcance incluye cualquier expresión lambda intermedia. - nota final]
último borrador
Una expresión lambda no tendrá una captura predeterminada o una captura simple en su introductor lambda a menos que su ámbito de inclusión más interno sea un ámbito de bloque ([basic.scope.block]) o aparezca dentro de un inicializador de miembro predeterminado y su ámbito de inclusión más interno scope es el ámbito de la clase correspondiente ([basic.scope.class]).
La redacción de C++17 es casi idéntica a la de C++14.
El programa está mal formado y el mensaje de diagnóstico es correcto. Los compiladores que no diagnostican la mala formación (icc, msvc) no se ajustan al estándar.
El programa se puede arreglar simplemente eliminando la captura predeterminada. De todos modos, no puede capturar nada fuera del alcance de un bloque, lo que lo hace inútil, por lo que no está permitido. Se puede acceder a elementos globales como ::g sin capturarlos.