c++ - sintaxis - ¿Por qué se permiten las lambdas genéricas mientras que las estructuras anidadas con métodos de plantillas no lo son?
sintaxis de c++ (2)
Supongo que el problema radica más en el estándar c ++
Correcto. Esto está estipulado en [temp] para plantillas de clases:
Una declaración de plantilla solo puede aparecer como un ámbito de espacio de nombres o una declaración de ámbito de clase.
y [temp.mem] para plantillas de miembros:
Una clase local de tipo sin cierre no debe tener plantillas de miembro.
¿Cuáles son las razones por las que a los lambdas se les permite tener miembros con plantillas y no a las estructuras locales?
Porque una vez que tuvimos lambdas en C ++ 11, se consideró que sería extremadamente útil ampliar ese concepto para tener lambdas genéricas. Hubo una proposal para dicha extensión de idioma, que fue revisada, revisada y adoptada.
Por otro lado, todavía no se ha presentado una propuesta (hasta donde tengo conocimiento de una breve búsqueda) que establece la motivación para la necesidad de plantillas de miembros en clases locales que no se resuelven adecuadamente mediante una lambda genérica. .
Si cree que se trata de un problema importante que debe resolverse, siéntase libre de enviar una propuesta después de exponer una motivación reflexiva sobre por qué las plantillas de miembros locales son importantes.
Por lo que yo entiendo, las lambdas genéricas se transforman en objetos de estructuras de alcance local con el operator()
plantilla operator()
. Esto hace que la lambda genérica sea una herramienta muy poderosa y fácil de usar. Por otro lado, uno puede crear estructuras anidadas en la función, cuando la estructura tiene miembro con plantilla, por ejemplo:
#include <iostream>
int main() {
struct inner {
template <class T>
void operator()(T &&i) { }
};
return 0;
}
o está modelado por sí mismo:
int main() {
template <class T>
struct inner {
void operator()(T &&i) { }
};
return 0;
}
el compilador parece tener un problema al compilarlo:
error: invalid declaration of member template in local class
y
error: a template declaration cannot appear at block scope
Supongo que el problema radica más en el estándar c ++ que en el error del compilador. ¿Cuáles son las razones por las que a los lambdas se les permite tener miembros con plantillas y no a las estructuras locales?
Encontré esta pregunta , pero creo que la respuesta está pasada de moda (no creo que sea cierto ni siquiera para c ++ 11).
Esta es la cuestión central 728 , que se presentó antes de que las lambdas genéricas fueran una cosa.
Mencionaste lambdas genéricos y que eran idénticos a las clases locales con el template operator()
miembro correspondiente template operator()
. Sin embargo, en realidad no lo son, y las diferencias están relacionadas con las características de implementación. Considerar
template <typename T>
class X {
template <typename>
void foo() {
T t;
}
};
Y
template <typename T>
auto bar() {
return [] (auto) {T t;};
};
Instanciar estas plantillas con <void>
estará bien en el primer caso, pero mal formado en el segundo. ¿Por qué está bien en el primer caso? foo
no necesita ser instanciable para cada T
particular, sino solo uno de ellos (esto sería [temp.res]/(8.1) ).
¿Por qué mal formado en el segundo caso? El cuerpo genérico de lambda se crea una instancia, parcialmente, utilizando los argumentos de plantilla proporcionados. Y la razón para esta instanciación parcial es el hecho de que ...
... los ámbitos léxicos utilizados al procesar una definición de función son fundamentalmente transitorios, lo que significa que retrasar la instanciación de una parte de la definición de una plantilla de función es difícil de soportar.
( Richard Smith ) Debemos instanciar una cantidad suficiente de la "plantilla" local para hacerlo independiente del contexto local (que incluye los parámetros de plantilla de la plantilla de función adjunta).
Esto también está relacionado con la lógica de [expr.prim.lambda]/13 , que ordena que una entidad sea capturada implícitamente por una lambda si ...
nombra la entidad en una expresión potencialmente evaluada ([basic.def.odr]) donde la expresión completa adjunta depende de un parámetro lambda genérico declarado dentro del alcance de alcance de la expresión lambda .
Es decir, si tengo un lambda como [=] (auto x) {return (typename decltype(x)::type)a;}
, donde a
es una variable de ámbito de bloque de una función adjunta, independientemente de si x
'' s member typedef es void
o no, el elenco provocará una captura de a
, porque debemos decidir sobre esto sin esperar una invocación de la lambda. Para una discusión de este problema, vea la propuesta original sobre lambdas genéricos .
La conclusión es que posponer completamente la instanciación de una plantilla de miembro no es compatible con el modelo utilizado por (al menos una) implementación (es) principal (es), y dado que esa es la semántica esperada, la característica no se introdujo.
¿Esa era la motivación original para esta restricción? Se introdujo en algún momento entre enero y mayo de 1994, sin ningún documento que lo cubriera, por lo que solo podemos tener una idea aproximada de las nociones prevalecientes en la justificación de este documento de por qué las clases locales no deben ser argumentos de plantilla:
Las plantillas de clase y las clases generadas a partir de la plantilla son entidades de alcance global y no pueden referirse a entidades de ámbito local.
Quizás en aquel entonces, uno quería BESAR.