Cómo entender la resolución propuesta de # 1664

8

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 foono podía ser consultada por aspecto depende argumento arriba porque el argumento [] { }cuales espacio de nombres no es ni Jni K, asume la forma en function barcomo J::K::zip<long>(zap([] { }) /*default argument*/);, Así accroding a [ expr.prim.lambda] párrafo 3 el espacio de nombres de [] { }está en fuction bary en ese ámbito, no foose puede encontrar, por lo que la parte enfatizada es para este caso que considera el espacio de nombres [] { }dentro de zaplo mismo zap, significa que el espacio de nombres de [] { }es K, Ahora foose puede encontrar en el espacio de nombres principalJpor 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 funcno es dependiente, sin embargo, debe determinarse cada vez que testse 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 funcestá determinado #1y MSVC ha demostrado que funcestá 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 )

Jack X
fuente
1
"el mismo alcance ... que el de la plantilla de función futilizada" en realidad suena como que la especialización hipotética estaría dentro del espacio de nombres J::K, no dentro de la definición de bar, pero entonces no veo cómo la frase cambiada haría alguna diferencia.
Aschepler
Entiendo todo eso, pero no creo que hayamos concluido lo mismo de la redacción antes de la parte cambiada / en negrita.
Aschepler
1
"Ahora, comienzo una recompensa por una pregunta adicional". Así no es como funciona StackOverflow. Si tiene una nueva pregunta, haga una nueva pregunta. Una pregunta por pregunta. Revertí tu edición.
Barry
@Barry Creo que la pregunta actualizada pertenece a "Cómo entender la resolución propuesta de # 1664"
Jack X
Lo sentimos, debes publicar nuevas preguntas . Stack Overflow está aquí para construir un repositorio de preguntas útiles y sus respuestas para futuros visitantes , y ahora que su primera pregunta tiene una respuesta, vemos esta página como un todo. Respete el trabajo que el respondedor ha puesto en proporcionar una solución y coloque nuevas preguntas en nuevas publicaciones de preguntas. Alterar (expandir) el problema aquí, de lo contrario, invalidaría su respuesta y haría que su publicación fuera del tema (cerrada como "necesita más atención").
Martijn Pieters

Respuestas:

3

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 funclugar, su ejemplo ilustra las reglas habituales de búsqueda de nombres en las plantillas: no importa cuántas veces y de dónde test se instancia el argumento predeterminado, funcno es un nombre dependiente y, por lo tanto, encuentra func(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 fooser utilizado pero no definido, contradiciendo tanto el { }mensaje claramente visible como su mensaje de error anterior sobre no encontrarlo.

Arenque Davis
fuente
Trato de resumir mi comprensión sobre sus respuestas. Para el argumento predeterminado, si son nombres no dependientes, los nombres se enlazan como están escritos en ese punto (como dijo [dcl.fct.default] / 5), en cambio, los nombres dependientes son necesarios para buscar el nombre cada vez que la función llamado. 2.Para una expresión lambda, porque no es un nombre, por lo que no puede vincularse incluso si el argumento predeterminado no es dependiente, por lo que si no hay una parte enfatizada en la restricción, el tipo de cierre tendría un espacio de nombres diferente según el punto de llamada donde lo cubre el espacio de nombres. ¿Estás de acuerdo?
Jack X
Otra pregunta de sus respuestas que necesito hablar individualmente es: "La especialización de plantilla de función ficticia" se refiere a "J :: K :: zip <long> ();", en lugar de la definición de "J :: K :: zip <long> () "en el punto de instanciación?
Jack X
@jackX: Es eso y [temp.nondep] / 1 para nombres no dependientes; se buscan nombres dependientes para cada instanciación, ya sea que aparezcan o no en un argumento predeterminado. El tipo de cierre pertenecería a la especialización de plantilla de función ficticia (que no es una llamada ni una definición real de zip).
Davis Herring
entonces, ¿la "especialización de plantilla de función ficticia" se refiere a algo como "J :: K :: zip <long> ();"? y cuál es el efecto de "excepto que el alcance en el que se declara un tipo de cierre", creo que la oración lo siguió: "y, por lo tanto, sus espacios de nombres asociados - permanecen determinados por el contexto de la definición del argumento predeterminado" es suficiente a ADL
jack X
@jackX: acabo de decir que no se refiere así. El alcance es la regla real: los espacios de nombres son un corolario, incluso si llevan toda la importación.
Davis Herring