Pregunta

¿Qué parte de la especificación de C ++ restringe la búsqueda dependiente del argumento de la búsqueda de plantillas de función en el conjunto de espacios de nombres asociados? En otras palabras, ¿por qué el último llame? main a continuación no se compila?

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
    void non_template(foo const&) {}
}

int main() {
    ns::foo f;
    non_template(f); // This is fine.
    frob<0>(f); // This is not.
}
¿Fue útil?

Solución

Esta parte lo explica:

C ++ estándar 03 14.8.1.6:

Nota: Para nombres de funciones simples, la búsqueda dependiente del argumento (3.4.2) se aplica incluso cuando el nombre de la función no es visible dentro del alcance de la llamada. Esto se debe a que la llamada todavía tiene la forma sintáctica de una llamada de función (3.4.1). Pero cuando se usa una plantilla de función con argumentos de plantilla explícita, la llamada no tiene la forma sintáctica correcta a menos que haya una plantilla de función con ese nombre visible en el punto de la llamada. Si no se ve tal nombre, la llamada no es sintácticamente bien formada y la búsqueda dependiente de argumentos no se aplica. Si se puede ver algún nombre de este tipo, se aplica la búsqueda dependiente del argumento y las plantillas de función adicionales se pueden encontrar en otros espacios de nombres.

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);    //ill-formed: not a function call
  A::f<3>(b); //well-formed
  C::f<3>(b); //ill-formed; argument dependent lookup
              // applies only to unqualified names
  using C::f;
  f<3>(b);    //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}

Otros consejos

Me gustaría refinar la respuesta ligeramente aceptada. No está claro en la pregunta OP, pero la parte importante del estándar (citado por Kornel) es esto (énfasis mío):

Pero cuando una plantilla de función con Argumentos de plantilla explícita se usa, la llamada no tiene la forma sintáctica correcta

Entonces, lo que está prohibido es confiar en ADL y usar argumentos de plantilla explícitos. Desafortunadamente, el uso de argumentos de plantilla no de tipo requiere el uso de argumentos explícitos (a menos que tengan valores predeterminados).

A continuación se muestra el código de muestra que muestra esto.:

En Vivo

#include <string>
#include <utility>

namespace C {
  struct B { };
  template<class T> void f(T t){}
}

void g(C::B b) {
  f(b);           // OK
  //f<C::B>(b);   // ill-formed: not a function call, but only 
                  //  because explicit template argument were used

  std::string s;
  move(s);                      // OK
  //move<std::string&>(s);      // Error, again because 
                                //  explicit template argument were used
  std::move<std::string&>(s);   // Ok
}

int main()
{
 C::B b;
 g(b);
}

Desde C ++ 20, ADL también funciona bien con la plantilla de función explícita. Aquí está la propuesta:P0846R0: ADL y plantillas de funciones que no son visibles:

En lugar de requerir que el usuario use la palabra clave de la plantilla, se propuso una revisión de las reglas de búsqueda para que un nombre para el cual una búsqueda normal no produzca ningún resultado o encuentre una o más funciones y que es seguido por AA "<" tratado como Si se hubiera encontrado un nombre de plantilla de función y haría que se realice ADL.

Actualmente, solo GCC 9 ha implementado esta función, por lo que su ejemplo puede compilar.

live demo.

Editar: No, esto no es correcto. Ver @Respuesta de Kornel.


No estoy completamente seguro, pero después de haber consultado el "Language de programación C ++" de Stroustrup, creo que el Apéndice C Sección 13.8.4 puede que ser la causa.

Ya que frob es una plantilla que uno podría especializarse para i=0 En un punto después de llamarlo. Esto significa que la implementación se quedaría con dos posibles formas de elegir cuál frob para llamar como parece que puede elegirlo en el punto de instanciación o Al final del procesamiento de la unidad de traducción.

Entonces, creo que el problema es que podrías hacer

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
}

int main() {
    ns::foo f;
    frob<0>(f);
    return 0;
}

namespace ns {
    template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top