Pregunta gcc y clang instancian implícitamente los argumentos de la plantilla durante la resolución de sobrecarga del operador


Considera este código:

struct A; // incomplete type

template<class T>
struct D { T d; };

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;  // doesn't compile; complain that D<A>::d has incomplete type
    u.operator=(v); // compiles
}

Manifestación. Ya que u.operator=(v) compila pero u = v; no, en algún lugar durante la resolución de sobrecarga para la última expresión, el compilador debe haber instanciado implícitamente D<A> - pero no veo por qué se requiere esa instanciación.

Para hacer las cosas más interesantes, este código compila:

struct A; // incomplete type

template<class T>
struct D; // undefined

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;
    u.operator=(v);
}

Manifestación.

¿Que está pasando aqui? Por que u = v; causa la instanciación implícita de D<A> - un tipo que no se usa en ningún lugar en el cuerpo de Bdefinición de s en el primer caso, pero no el segundo?


32
2017-09-19 02:41


origen


Respuestas:


El punto entero del asunto es ADL Pateando:

N3797 - [basic.lookup.argdep]

Cuando la expresión postfix en una llamada a función (5.2.2) es una identificación no calificada, no se consideran otros espacios de nombres   durante la búsqueda no calificada habitual (3.4.1) se puede buscar, y en esos espacios de nombres, espacio de nombres-ámbito   Se pueden encontrar las declaraciones de plantilla de función o función de amigo (11.3) que no se encuentran visibles.

siguiendo:

Para cada tipo de argumento T en la llamada a función, hay un conjunto de cero o más espacios de nombres asociados y un   conjunto de cero o más clases asociadas para ser consideradas. [...] Los conjuntos de   espacios de nombres y clases se determinan de la siguiente manera:

  • Si T es un tipo de clase [..] sus clases asociadas son: ...   Además, si T es una especialización de plantilla de clase, sus espacios de nombres y clases asociados también incluyen: los espacios de nombres y las clases asociados con el   tipos de argumentos de plantilla proporcionados para los parámetros de tipo de plantilla

D<A> es una clase asociada y por lo tanto en la lista esperando su turno.

Ahora para la parte interesante [temp.inst] / 1

A menos que una especialización de plantilla de clase haya sido explícitamente instanciada (14.7.2) o explícitamente especializada (14.7.3),   la especialización de plantilla de clase se instancia de forma implícita [...] cuando la integridad del tipo de clase afecta la semántica del programa

Uno podría pensar que la integridad del tipo D<A> no afecta en absoluto la semántica de ese programa, sin embargo [basic.lookup.argdep] / 4 dice:

Al considerar un espacio de nombre asociado, la búsqueda es la misma que la búsqueda realizada cuando el espacio de nombre asociado se utiliza como un calificador (3.4.3.2)   excepto eso:

[...]   Todas las funciones de amigo de ámbito de nombre de espacio o plantillas de función de amigo declaradas en clases asociadas son visibles dentro de sus respectivas   espacios de nombres incluso si no son visibles durante una búsqueda ordinaria (11.3)

es decir, la integridad del tipo de clase en realidad afecta Declaraciones de amigos -> la integridad del tipo de clase afecta, por lo tanto, a la semántica del programa. Esa es también la razón por la cual su segunda muestra funciona.

TL; DR  D<A> es instanciado.

El último punto interesante se refiere a por qué ADL comienza en primer lugar por

u = v; // Triggers ADL
u.operator=(v); // Doesn't trigger ADL

§13.3.1.2 / 2 dicta que no puede haber no miembros operator=(que no sean los integrados). Únete a esto en [over.match.oper] / 2:

El conjunto de candidatos no miembros es el resultado de la búsqueda no calificada del operador @ en el contexto   de la expresión de acuerdo con las reglas habituales para la búsqueda de nombres en llamadas a funciones no calificadas (3.4.2)   excepto que todas las funciones miembro son ignoradas.

y la conclusión lógica es: no tiene sentido realizar la búsqueda de ADL si no hay una forma de no miembro en la tabla 11. Sin embargo, [temp.inst] p7 relaja esto:

Si el proceso de resolución de sobrecarga puede determinar la función correcta para llamar sin crear una instancia de una definición de plantilla de clase, no se especifica si la instanciación realmente tiene lugar.

y esa es la razón por la cual clang desencadenó la totalidad ADL -> implicit instantiation proceso en primer lugar.

Empezando desde r218330 (al momento de escribir esto, se ha confirmado hace unos minutos) este comportamiento se modificó para no realizar ADL para operator= en absoluto.


Referencias

Gracias a Richard Smith y David Blaikie por ayudarme a descifrar esto.


19
2017-09-23 22:09



Bueno, creo que en Visual Studio 2013 el código debería verse como (sin = nullptr):

  struct A; // incomplete type

  template<class T>
  struct D { T d; };

  template <class T>
  struct B { int * p; };

  int void_main() {
    B<D<A>> u, v;
    u = v;          //  compiles
    u.operator=(v); // compiles
    return 0;
    }

En este caso, debería compilarse bien simplemente porque se pueden usar tipos incompletos para el uso de especialización de clases de plantillas específicas.

En cuanto al error en tiempo de ejecución, la variable v utilizada sin inicialización, es correcta, la estructura B no tiene ningún constructor => B :: p no está inicializado y podría contener basura.


-2
2017-09-23 16:14