2016-05-04 18 views
6

Ho appena incontrato alcuni comportamenti interessanti con la ricerca argomento-dipendente, che non capiscono fino in fondo:ricerca Argument-dependent comportarsi in modo imprevisto sui tipi alias da un altro spazio dei nomi

#include <iostream> 

namespace a { 
struct Foo { 
    Foo(int v1, int v2) : v1(v1), v2(v2) { } 
    int v1,v2; 
}; 
} 

namespace b { 
template <typename T> 
struct Baz : T { 
    using T::T; 
}; 
} 

namespace c { 
using Foo = ::b::Baz< ::a::Foo>; 

// (1) NOT FOUND BY ADL 
// std::ostream& operator << (std::ostream& os, const Foo& foo) 
// { 
// return os << foo.v1 << "," << foo.v2; 
// } 
} 

namespace b { 

// (2) FOUND BY ADL 
std::ostream& operator << (std::ostream& os, const ::c::Foo& foo) 
{ 
    return os << foo.v1 << "," << foo.v2; 
} 

} 

int main() 
{ 
    c::Foo foo(1,2); 
    // Variant (1): ADL fails: should it not find 
    // c::operator<<(std::ostream&, const Foo&) ? 
    // Variant (2) compiles 
    std::cout << "foo: " << foo << std::endl; 
} 

ottengo che c::Foo è in realtà b::Baz<...>, quindi in qualche modo ha senso che ADL trovi l'operatore quando lo definisco all'interno di namespace b. Ma sembra sfidare l'intuizione che la definizione dell'operatore all'interno di namespace c non funzioni, poiché c::Foodovrebbe (IMHO) consentire al compilatore di eseguire ADL anche all'interno di namespace c.

Perché non è così? Qual è la logica alla base di questo?

+0

@mindriot: "* Ma che sembra come un dettaglio di implementazione *" No, non lo è. * In nessun luogo * in C++ è possibile utilizzare un alias di tipo in un modo che si comporterebbe in un modo diverso rispetto a quando si è utilizzato il tipo effettivo. Questo non è un "dettaglio di implementazione"; questo è il comportamento richiesto per i tipi di alias. –

+0

@NicolBolas Sono d'accordo, ma non è quello che intendevo. Forse dovrei riformulare: Quella particolare _esplanation_ ("immagina che il riferimento venga riscritto") fa sembrare che sia solo un dettaglio di implementazione. La ragione per cui Columbo ha dato nella sua risposta ha più senso. – mindriot

risposta

4

[basic.lookup.argdep]/2:

nomi typedef e dichiarazione using s utilizzati per specificare i tipi non contribuiscono a questo insieme.

Né i nomi typedef né le dichiarazioni di utilizzo sono affiliati al tipo che designano. Se lo fossero (che sarebbe effettivamente controintuitivo, IMO), le cose si spezzerebbero molto facilmente; solo perché ho typedef qualche classe, tutte le chiamate stanno ora valutando funzioni aggiunte in prossimità del typedef, che è praticamente mai desiderato:

#include <string> 

namespace A { 
    using str = std::string; 
    int stoi(str); // This will be a candidate in e.g. stoi(std::string{"0.4"}), 
        // leading to potentially different behavior or ambiguity 

namespace B { 
    int stoi(std::string); // This is no candidate, because...? 
} 
+0

Grazie, è quello che mi aspettavo: sono esplicitamente tralasciati. Hai qualche idea sulla logica? Per esempio, qualcosa si romperà, o diventerà eccessivamente complicato da implementare, se questa particolare frase fosse stata omessa? – mindriot

+0

@mindriot Vedi modifica. – Columbo

+0

Grazie, questo ha senso! – mindriot