2010-09-09 27 views
7

Me ha picado un par de veces este problema y también mis colegas. Al compilarLlamada ambigua a la función de plantilla debido a ADL

#include <deque> 
#include <boost/algorithm/string/find.hpp> 
#include <boost/operators.hpp> 

template< class Rng, class T >  
typename boost::range_iterator<Rng>::type find(Rng& rng, T const& t) { 
     return std::find(boost::begin(rng), boost::end(rng), t); 
} 

struct STest { 
     bool operator==(STest const& test) const { return true; } 
}; 

struct STest2 : boost::equality_comparable<STest2> { 
     bool operator==(STest2 const& test) const { return true; } 
}; 

void main() { 
     std::deque<STest> deq; 
     find(deq, STest()); // works 
     find(deq, STest2()); // C2668: 'find' : ambiguous call to overloaded function 
} 

... el compilador VS9 falla al compilar el segundo hallazgo. Esto se debe al hecho de que STest2 hereda de un tipo que está definido en el espacio de nombres boost que hace que el compilador pruebe ADL que encuentra boost::algorithm::find(RangeT& Input, const FinderT& Finder).

Una solución obvia es prefijar la llamada a find(…) con "::", pero ¿por qué es necesario? Existe una coincidencia perfectamente válida en el espacio de nombres global, entonces ¿por qué invocar Argument-Dependent Lookup? ¿Alguien puede explicar el razonamiento aquí?

+0

Tenga en cuenta que la coincidencia no es perfecta. Una función que no sea de plantilla 'find (std :: deque , STest);' sería una mejor coincidencia. – MSalters

+0

@MSalters: tienes razón, por supuesto. La coincidencia en el espacio de nombres global no es perfecta. Creo que esperaba que ADL fuera una excepción, y solía encontrar una coincidencia estrictamente mejor en un espacio de nombres más remoto. Las respuestas muestran que esto es un malentendido. – Sebastian

+0

Mi pregunta es por qué ADL está encontrando una función dentro de 'boost :: algorithm' cuando el argumento es una clase en el espacio de nombres global, que hereda de una clase en el espacio de nombres' boost :: detail' ... y la respuesta es una 'using algorithm :: find' en el encabezado' 'que lleva la función al espacio de nombres' boost'. ¿Podría ser esto un defecto? ¿No debería ser esto al menos opcional? –

Respuesta

7

ADL no es un mecanismo de reserva para utilizar cuando "normal" resolución de la sobrecarga de falla, funciones que se encuentran por ADL son tan viables como funciones que se encuentran por búsqueda normal.

Si ADL era una solución alternativa, entonces podría caer fácilmente en la trampa si se usara una función incluso cuando había otra función que era una mejor coincidencia pero solo visible a través de ADL. Esto parecería especialmente extraño en el caso de (por ejemplo) sobrecargas del operador. No querría que se compararan dos objetos a través de un operator== para los tipos a los que podrían convertirse implícitamente cuando exista un operator== perfectamente bueno en el espacio de nombre apropiado.

+0

Gracias, tienes razón. Consideré que ADL era la excepción y no la regla, y solía encontrar coincidencias estrictamente mejores. Entonces, con su argumento, el compilador debe buscar en otros espacios de nombres para encontrar una mejor coincidencia, pero si busca y encuentra una coincidencia indistinguible, lo más sensato es fallar con un error. – Sebastian

3

Voy a añadir lo obvio contestar a mí mismo porque acabo de hacer una investigación sobre este problema:

C++ 03 3.4.2

§ 2. Para cada tipo de argumento T en la llamada de función , hay un conjunto de cero o más espacios de nombres asociados [...] los conjuntos de espacios de nombres y las clases se determinan de la siguiente manera:

[...]

- Si T es un tipo de clase (incluidos los sindicatos), su asociación ted clases son: la clase en sí; la clase de la cual es un miembro , si existe; y sus clases de base directa e indirecta. Sus espacios de nombres asociados son los espacios de nombres en los que se definen sus clases asociadas.

§ 2a Si la búsqueda no cualificada ordinaria del nombre se encuentra la declaración de una función miembro de la clase, los asociados espacios de nombres y clases no se consideran. De lo contrario, el conjunto de declaraciones encontrado por la búsqueda el nombre de la función es la unión del conjunto de declaraciones encontradas utilizando la búsqueda ordinaria no calificada y el conjunto de las declaraciones encontradas en los espacios de nombres y las clases asociadas a los tipos de argumentos.

Al menos cumple con los estándares, pero sigo sin entender el motivo aquí.

3

Considere un mystream que hereda de std::ostream. Desea que su tipo sea compatible con todos los operadores << que se definen para std::ostream normalmente en el espacio de nombres estándar. Entonces las clases base son clases asociadas para ADL.

Creo que esto también se desprende del principio de sustitución, y las funciones en el espacio de nombres de una clase se consideran parte de su interfaz (ver "¿Qué hay en una clase?" De Herb Sutter). Entonces, una interfaz que funciona en la clase base debe seguir trabajando en una clase derivada.

También puede evitar este desactivando ADL:

(find)(deq, STest2()); 
+0

El argumento del operador que Charles hizo también es convincente, por supuesto. No es realmente sorprendente que los espacios de nombres de clase base se consideren para ADL. En mi ejemplo del mundo real, es el espacio de nombres del argumento de la plantilla el que se incluye.Muy sorprendente, al menos hasta que leí el estándar :-) – Sebastian

1

creo que declaró el problema usted mismo:

en el espacio de nombres global

Funciones en el espacio de nombres global se consideran pasado. Es el alcance más externo por definición. Cualquier función con el mismo nombre (no necesariamente aplicable) que se encuentre en un alcance más cercano (desde el punto de vista de la llamada) se seleccionará primero.

template <typename Rng, typename T> 
typename Rng::iterator find(Rng& rng, T const& t); 

namespace foo 
{ 
    bool find(std::vector<int> const& v, int); 

    void method() 
    { 
    std::deque<std::string> deque; 
    auto it = find(deque, "bar"); 
    } 
} 

aquí (a menos vector o deque incluyen algorithm, que se permite), el único método que se recogió durante el nombre de consulta será:

bool foo::find(std::vector<int> const&, int); 

Si algorithm alguna manera está incluido, también habrá:

template <typename FwdIt> 
FwdIt std::find(FwdIt begin, FwdIt end, 
       typename std::iterator_traits<FwdIt>::value_type const& value); 

Y, por supuesto, la resolución de sobrecarga fallará indicando que no hay coincidencia.

Tenga en cuenta que la búsqueda de nombre es extremadamente tonta: no se consideran ni la aridad ni el tipo de argumento.

Por lo tanto, sólo hay dos tipos de libres funciones que se deben utilizar en C++:

  • los que forman parte de la interfaz de una clase, declarado en el mismo espacio de nombres, recogido por ADL
  • las que no lo son, y que usted debe calificar explícitamente para evitar problemas de este tipo

Si usted se encuentra fuera de estas reglas, que podría funcionar, o no, dependiendo de lo que está incluido, y eso es muy difícil.

+0

Mi ejemplo es ligeramente diferente al tuyo. En mi ejemplo, ADL encuentra una implementación en un espacio de nombres del que el llamante no forma parte. – Sebastian

+0

@Sebastian: "llamador" es ambiguo. Hay dos cosas a considerar: el punto de llamada en sí y los argumentos. Mi ejemplo demostró ambos, aunque no lo hice más complicado usando herencia. –

Cuestiones relacionadas