El siguiente programa compila bien con g ++ (versión 10.1.0) pero no con clang ++ (10.0.0)

#include <iostream>

template <typename U>
struct A { U x; };

namespace tools {
  template <typename U>
  void operator+=(A<U>& lhs, const A<U>& rhs) { lhs.x += rhs.x; }
}

namespace impl {
  template <typename U = int>
  void f() {
    A<U> a{3};
    A<U> b{2};
    a += b;
    std::cout << a.x << std::endl;
  }
}

namespace impl {
  using namespace tools;
}

int main()
{
  impl::f();
}

El error es:

name.cpp:16:7: error: no viable overloaded '+='
    a += b;
    ~ ^  ~
name.cpp:27:9: note: in instantiation of function template specialization 'impl::f<int>' requested here
  impl::f();

Claramente, mover la parte using namespace tools antes de la función de plantilla impl::f() elimina el error de sonido metálico.

Nota adicional Un punto importante aquí es que f es una función de plantilla. Sin parámetros de plantilla, el código no se compila ni con gcc ni con clang.

¿Qué compilador es correcto aquí? gcc o clang?

16
francesco 23 jun. 2020 a las 11:10

2 respuestas

La mejor respuesta

El código está mal formado porque la parte de la búsqueda de nombre no calificada que no depende del argumento se realiza en el contexto de definición de plantilla. Entonces, Clang tiene razón y el error de GCC ya se informó (error n. ° 70099 )

Lo que sigue es la larga explicación.

Dentro de su código de ejemplo hay algún lugar que debe marcarse para permitir la discusión:

namespace impl {
  template <typename U = int>
  void f() {                       // (1) point of definition of the template f
    A<U> a{3};
    A<U> b{2};
    a += b;                        //  call operator += with arguments of dependent type A<U> 
    std::cout << a.x << std::endl;
  }
}

namespace impl {
  using namespace tools;          // using directive     
}

int main()
{
  impl::f();
}                                 // (2) point of instantiation of impl::f<int>

En la definición de la plantilla f (1), la llamada al operador + = se realiza con argumentos de tipo A<U>. A<U> es un tipo dependiente, así que {{ X3}} es un nombre dependiente.

[temp.dep.res] / 1 describe cómo se busca operator +=:

Para una llamada de función donde la expresión postfix es un nombre dependiente, las funciones candidatas se encuentran utilizando las reglas de búsqueda habituales del contexto de definición de plantilla ([basic.lookup.unqual], [basic.lookup.argdep]). [Nota: Para la parte de la búsqueda usando espacios de nombres asociados ([basic.lookup.argdep]), esta búsqueda encuentra las declaraciones de función que se encuentran en el contexto de creación de instancias de plantilla, como se describe en [basic.lookup.argdep]. - nota final] [...]

Hay dos búsquedas que se realizan.

Búsqueda de nombre no calificado que no depende del argumento [basic.lookup.unqual] .

Esta búsqueda se realiza desde el contexto de definición de plantilla . " del contexto de definición de plantilla " significa el contexto en el punto de definición de la plantilla. El término " contexto " se refiere al contexto de búsqueda. Si la plantilla f se declara por primera vez en el espacio de nombres impl y luego se define en el ámbito del espacio de nombres global, la búsqueda de nombre no calificada aún encontraría miembros del espacio de nombres impl. Esta es la razón por la cual la regla [temp.dep.res] / 1 usa " el contexto de definición de plantilla " y no simplemente " punto de definición de plantilla ".

Esta búsqueda se realiza desde (1) y no encuentra el operator += definido en el espacio de nombres tools. La directiva de uso aparece después de (1) y no tiene ningún efecto.

Búsqueda de nombre dependiente del argumento (ADL) [basic.lookup.argdep]

La ADL se realiza en el punto de instanciación (2). Entonces se realiza después de la directiva de uso. Sin embargo, ADL solo considera el espacio de nombres asociado al tipo de argumentos. Los argumentos tienen el tipo A<int>, la plantilla A es un miembro del espacio de nombres global, por lo que ADL solo puede encontrar miembros de este espacio de nombres.

En (2) no hay operator += declarado en el ámbito del espacio de nombres global. Por lo tanto, ADL tampoco puede encontrar una declaración para operator +=.

7
Oliv 23 jun. 2020 a las 16:54

Parece que clang está aquí de acuerdo con esto. En resumen: está extendiendo su espacio de nombres pero using namespace debería 'propagarse' a esta extensión solo hacia adelante.

Una directiva de uso especifica que los nombres en el espacio de nombres nominado se pueden usar en el ámbito en el que la directiva de uso aparece después de la directiva de uso. Durante la búsqueda de nombres no calificados ([basic.lookup.unqual]), los nombres aparecen como si se hubieran declarado en el espacio de nombres adjunto más cercano que contiene la directiva using y el espacio de nombres nominado. [Nota: en este contexto, "contiene" significa "contiene directa o indirectamente". - nota final]

7
bartop 23 jun. 2020 a las 09:15