Después de mirar la resolución propuesta de # 1664 ( resolución propuesta 1664 ), estoy confundido por las reglas de un argumento predeterminado de una plantilla de función. Cite el contenido aquí:
De acuerdo con 8.1.5 [expr.prim.lambda] párrafo 3
El tipo de cierre se declara en el ámbito de bloque más pequeño, ámbito de clase o ámbito de espacio de nombres que contiene la expresión lambda correspondiente. [Nota: Esto determina el conjunto de espacios de nombres y clases asociados con el tipo de cierre (6.4.2 [basic.lookup.argdep]). Los tipos de parámetros de un declarador lambda no afectan estos espacios de nombres y clases asociados. —Nota final]
Sin embargo, el párrafo 13 de 17.8.1 [temp.inst] dice
Si se llama a una plantilla de función f de una manera que requiere el uso de un argumento predeterminado, se buscan los nombres dependientes, se verifican las restricciones semánticas y la instanciación de cualquier plantilla utilizada en el argumento predeterminado se realiza como si el argumento predeterminado había sido un inicializador utilizado en una especialización de plantilla de función con el mismo alcance, los mismos parámetros de plantilla y el mismo acceso que el de la plantilla de función f utilizada en ese punto.
Una posibilidad, entonces, es que el tipo de cierre para la expresión lambda en un argumento predeterminado para una función de plantilla (o, presumiblemente, una función miembro de una plantilla de clase) debe considerarse como declarado en algún ámbito de bloque en el cuerpo de La especialización de plantilla de funciones ficticias.
Considere el siguiente ejemplo:
namespace J {
inline namespace K {
template <typename T> int zap(const T &t) { foo(t); return 0; }
template <typename T> void zip(int = zap([] { })) { }
}
template <typename T> void foo(const T &) { }
}
void bar() {
J::K::zip<long>();
/*Accroding to the above wording,the invoke just like:
=> J::K::zip<long>(zap([] { }));
*/
}
Si zip no fuera una plantilla, la búsqueda dependiente de argumentos resuelve con éxito la búsqueda de foo en todas las implementaciones probadas; sin embargo, hay una variación de implementación en el manejo del ejemplo tal como está escrito.
Resolución propuesta (septiembre de 2013):
Cambie 17.8.1 [temp.inst] párrafo 13 de la siguiente manera:
si se llama a una plantilla de función f de una manera que requiere un argumento predeterminado, se buscan los nombres dependientes, se comprueban las restricciones semánticas y se crea una instancia de cualquier plantilla utilizada en el argumento predeterminado se realiza como si el argumento predeterminado hubiera sido un inicializador utilizado en una especialización de plantilla de función con el mismo alcance, los mismos parámetros de plantilla y el mismo acceso que el de la plantilla de función f utilizada en ese punto, excepto que el alcance en el que se declara un tipo de cierre (8.1.5 [expr.prim.lambda]) - y, por lo tanto, sus espacios de nombres asociados - permanecen según lo determinado en el contexto de la definición del argumento predeterminado. Este análisis se llama instanciación de argumento por defecto. El argumento predeterminado instanciado se usa como argumento de f.
Tenga en cuenta la parte enfatizado, si yo no me malinterpreten, Esto significa que si la parte comentario subrayado hacia fuera, el foo
no podía ser consultada por aspecto depende argumento arriba porque el argumento [] { }
cuales espacio de nombres no es ni J
ni K
, asume la forma en function bar
como J::K::zip<long>(zap([] { }) /*default argument*/);
, Así accroding a [ expr.prim.lambda] párrafo 3 el espacio de nombres de [] { }
está en fuction bar
y en ese ámbito, no foo
se puede encontrar, por lo que la parte enfatizada es para este caso que considera el espacio de nombres [] { }
dentro de zap
lo mismo zap
, significa que el espacio de nombres de [] { }
es K
, Ahora foo
se puede encontrar en el espacio de nombres principalJ
por reglas de búsqueda dependientes de argumentos, hasta ahora, si no entiendo estas reglas, corríjame. El otro punto de vista es que el argumento predeterminado se evalúa cada vez que se llama a la función, incluso si el valor predeterminado no es dependiente . siguiente código:
#include <iostream>
struct A {
};
template<typename T>
int func(T, float) { //#a
std::cout << "float" << std::endl;
return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1
}
template<typename T>
int func(T, int) { //#b
std::cout << "int" << std::endl;
return 0;
}
int main() {
test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
std::getchar();
}
Aunque el argumento predeterminado func
no es dependiente, sin embargo, debe determinarse cada vez que test
se llama a la función y pruebo el código en algunos cumplidores.
Toda la versión del informe MSVC "int", informe gcc "float", informe clang "float", ¿qué demonios? Según el informe de gcc o clang, parece que func
está determinado #1
y MSVC ha demostrado que func
está determinado en #2
. si MSVC está mal, eso significa que el argumento predeterminado no dependiente podría determinarse dentro del # 1 y no es necesario determinar cada vez que se llama la función, ¿por qué la parte enfatizada debe agregarse?Si entiendo la parte enfatizada correctamente, el propósito es mantener el espacio de nombres del tipo de cierre dentro del argumento predeterminado consistente si la expresión lambda está en el punto de declaración de función o en el punto de llamada ). Si no entiendo estas reglas, ¿cómo interpretarlas correctamente?
ACTUALIZAR:
la versión 9.1 o superior de gcc no puede compilar el código mencionado en # 1664, informará un error ( el resultado de la compilación )
Preguntas:
1. ¿El argumento predeterminado no dependiente de plantilla de función o función no de plantilla necesita determinarse cada vez que se llama a la función correspondiente?
2. ¿Qué significa "la definición para el argumento por defecto" media? ¿Es esta redacción estricta? ( En otras palabras, a mi entender, lo actullay las reglas añadidas quieren expresar es que el espacio de nombres del tipo closeure es el de una función donde Declartion contiene un argumento predeterminado que contiene la expresión lambda correspondiente, ¿verdad? Si no entiendo bien, corríjame )
fuente
f
utilizada" en realidad suena como que la especialización hipotética estaría dentro del espacio de nombresJ::K
, no dentro de la definición debar
, pero entonces no veo cómo la frase cambiada haría alguna diferencia.Respuestas:
Los argumentos predeterminados se evalúan cada vez que se llaman, pero esta es una propiedad de tiempo de ejecución: las llamadas no se cuentan por la línea de origen sino por el flujo de control real. Por separado, un argumento predeterminado para una función con plantilla se considera una definición y se instancia cuando es necesario, hasta una vez por especialización de la función (con la condición habitual sobre múltiples puntos de instanciación que deben coincidir). CWG1664 fue un tema muy limitado basado en cómo se redactó esa instanciación : al introducir una plantilla de función ficticia , dejó abierta la posibilidad de que la declaración de la lambda se moviera "físicamente". La solución realmente afecta solo a ADL.
En su
func
lugar, su ejemplo ilustra las reglas habituales de búsqueda de nombres en las plantillas: no importa cuántas veces y de dóndetest
se instancia el argumento predeterminado,func
no es un nombre dependiente y, por lo tanto, encuentrafunc(T,float)
(cada vez). MSVC nunca ha implementado esta regla correctamente (porque, para ser justos, su implementación es anterior a la regla y solo recientemente comenzaron la reescritura necesaria (y casi completa) de su soporte de plantilla).Mientras tanto, el GCC reciente está claramente defectuoso con el ejemplo CWG1664: tenga en cuenta que se queja de
foo
ser utilizado pero no definido, contradiciendo tanto el{ }
mensaje claramente visible como su mensaje de error anterior sobre no encontrarlo.fuente
zip
).