2010-09-09 15 views
7

Sono stato morso da questo problema un paio di volte e così ho i miei colleghi. Durante la compilazioneChiamata ambigua alla funzione di modello a causa di 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 
} 

... il compilatore VS9 non riesce durante la compilazione della seconda ricerca. Ciò è dovuto al fatto che STest2 eredita da un tipo definito nello spazio dei nomi boost che attiva il compilatore per provare ADL che trova boost::algorithm::find(RangeT& Input, const FinderT& Finder).

Una soluzione ovvia è quella di anteporre la chiamata a find(…) con "::", ma perché questo è necessario? C'è una corrispondenza perfettamente valida nello spazio dei nomi globale, quindi perché invocare la ricerca dipendente dall'argomento? Qualcuno può spiegare la logica qui?

+0

Si noti che la partita non è perfetta. Una funzione non-template 'find (std :: deque , STest)' sarebbe una corrispondenza migliore. – MSalters

+0

@MSalters: hai ragione, naturalmente. La corrispondenza nello spazio dei nomi globale non è perfetta. Penso che mi aspettavo che ADL fosse un'eccezione, utilizzata per trovare una corrispondenza strettamente migliore in uno spazio dei nomi più remoto. Le risposte mostrano che questo è un fraintendimento. – Sebastian

+0

La mia domanda è perché ADL sta trovando una funzione all'interno di 'boost :: algorithm' quando l'argomento è una classe nello spazio dei nomi globale, che eredita da una classe nello spazio dei nomi' boost :: detail' ... e la risposta è una 'usando l'algoritmo :: find' nell'intestazione' 'che porta la funzione nello spazio dei nomi' boost'. Potrebbe essere un difetto? Questo non dovrebbe essere per lo meno opzionale? –

risposta

7

ADL non è un meccanismo di fallback da utilizzare quando "normale" la risoluzione di sovraccarico fallisce, funzioni che si trovano da ADL sono altrettanto vitale, come le funzioni trovate da ricerca normale.

Se ADL era una soluzione alternativa, si potrebbe facilmente cadere nella trappola in caso di utilizzo di una funzione anche in presenza di un'altra funzione più adatta, ma visibile solo tramite ADL. Ciò sembrerebbe particolarmente strano nel caso (ad esempio) di sovraccarichi dell'operatore. Non si desidera confrontare due oggetti tramite un operator== per i tipi che potrebbero essere convertiti implicitamente in quando esiste uno operator== perfettamente valido nello spazio dei nomi appropriato.

+0

Grazie, hai ragione. Avevo considerato ADL l'eccezione piuttosto che la regola, usata per trovare corrispondenze strettamente migliori forse. Quindi, con il tuo argomento, il compilatore deve cercare altri spazi dei nomi per trovare una corrispondenza migliore, ma se cerca e trova una corrispondenza indistinguibile, fallire con un errore è la cosa più sensata da fare. – Sebastian

3

io aggiungo l'ovvia me rispondere, perché ho appena fatto qualche ricerca su questo problema:

C++ 03 3.4.2

§2 Per ogni tipo di argomento T nella chiamata di funzione , v'è un insieme di zero o più spazi dei nomi associati [...] I set di namespace e classi vengono determinati nel modo seguente:

[...]

- Se T è un tipo di classe (compresi i sindacati), la sua associazione le classi sono: la classe stessa; la classe di cui è membro , se presente; e le sue classi di base dirette e indirette. Gli spazi dei nomi associati sono gli spazi dei nomi in cui sono definite le classi associate.

§ 2a Se l'ordinario ricerca non qualificato del nome trova la dichiarazione di una funzione membro della classe, gli associati namespace e classi non sono considerati. Altrimenti l'insieme di dichiarazioni trovate dalla ricerca di il nome della funzione è l'unione dell'insieme di dichiarazioni trovate usando ordinarie ricerche non qualificate e l'insieme di dichiarazioni trovate negli spazi dei nomi e classi associate ai tipi di argomento.

Almeno è conforme standard, ma ancora non capisco la logica qui.

3

Considerare uno mystream che eredita da std::ostream. Vorresti che il tuo tipo supportasse tutti gli operatori << definiti per lo standard std::ostream nello spazio dei nomi std. Quindi le classi base sono classi associate per ADL.

Penso che questo derivi anche dal principio di sostituzione - e le funzioni nel namespace di una classe sono considerate parte della sua interfaccia (vedi Herb Sutter "What's in a class?"). Quindi un'interfaccia che funziona sulla classe base dovrebbe continuare a funzionare su una classe derivata.

È inoltre possibile aggirare il problema disattivando ADL:

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

L'argomentazione dell'operatore che Charles ha dimostrato è ovviamente convincente. Non è davvero sorprendente che gli spazi dei nomi delle classi di base siano considerati per ADL. Nel mio esempio del mondo reale è incluso lo spazio dei nomi dell'argomento template.Molto sorprendente, almeno fino a quando ho letto lo standard :-) – Sebastian

1

penso che lei ha dichiarato il problema da soli:

nel namespace globale

Funzioni nel namespace globale sono considerate scorso. È lo scopo più esterno per definizione. Qualsiasi funzione con lo stesso nome (non necessariamente applicabile) che si trova in un ambito più vicino (dal punto di vista della chiamata) verrà raccolta per prima.

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"); 
    } 
} 

Qui (a meno che vector o deque includono algorithm, che è consentito), l'unico metodo che verrà raccolto durante nome look-up sarà:

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

Se algorithm è in qualche modo compreso, ci sarà anche:

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

E, naturalmente, la risoluzione di sovraccarico fallirà affermando che non c'è corrispondenza.

Si noti che la ricerca del nome è estremamente stupida: non vengono considerati né il tipo di argomento né il tipo di argomento!

Quindi, ci sono solo due tipi di free-funzioni che si dovrebbe usare in C++:

  • quelli che fanno parte dell'interfaccia di una classe, dichiarata nello stesso namespace, raccolto da ADL
  • quelle che non sono, e che si dovrebbe esplicitamente qualificato per evitare problemi di questo tipo

Se si cade fuori di queste regole, potrebbe funzionare, o meno, a seconda di ciò che è incluso, e questo è molto imbarazzante.

+0

Il mio esempio è leggermente diverso dal tuo. Nel mio esempio, ADL trova un'implementazione in uno spazio dei nomi di cui il chiamante non fa parte. – Sebastian

+0

@Sebastian: "chiamante" è ambiguo. Ci sono due cose da considerare: il punto di chiamata stesso e gli argomenti. Il mio esempio ha dimostrato entrambi, anche se non l'ho reso più complicato usando l'ereditarietà. –

Problemi correlati