2016-05-04 6 views
6

ich gerade lief in einige interessante Verhalten mit dem Argument abhängigen Lookup verhalten, die ich nicht ganz verstehen:Argument-dependent Nachschlag unerwartet auf Typen aliased aus einem anderen Namespace

#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; 
} 

ich, dass c::Foo tatsächlich b::Baz<...> ist, also macht es etwas Sinn, dass ADL den Operator findet, wenn ich ihn innerhalb namespace b definiere. Aber es scheint der Intuition zu widersprechen, dass das Definieren des Operators innerhalb von namespace c nicht funktioniert, da c::Foo (IMHO) dem Compiler erlauben sollte, ADL innerhalb namespace c auch durchzuführen.

Warum ist das nicht der Fall? Was ist der Grund dafür?

+0

@mindriot: „* Aber das scheint wie eine Implementierung Detail *“ Nein, ist es nicht. * Nirgendwo * in C++ dürfen Sie einen Typ-Alias ​​so verwenden, dass er sich anders verhält, als wenn Sie den tatsächlichen Typ verwendet hätten. Das ist kein "Implementierungsdetail"; das ist erforderlich für Typaliase. –

+0

@ NicolBolas Ich stimme zu, aber das ist nicht, was ich meinte. Vielleicht sollte ich anders formulieren: Diese spezielle _explanation_ ("stelle dir vor, dass die Referenz neu geschrieben wird") lässt es klingen, als wäre es nur ein Implementierungsdetail. Der Grund, den Columbo in seiner Antwort gab, macht mehr Sinn. – mindriot

Antwort

4

[basic.lookup.argdep]/2:

Typedef Namen und mit Deklaration s verwendet, um die Typen zu spezifizieren nicht zu diesem Set beitragen.

Weder typedef-Namen noch using-declarations gehören zu dem Typ, den sie bezeichnen. Wenn sie das wären (was eigentlich kontraintuitiv wäre, IMO), würden die Dinge sehr leicht brechen; nur weil ich eine Klasse typedef'd, werden alle Anrufe nun Funktionen unter Berücksichtigung in der Nähe des typedef gegeben, die praktisch nie gewünscht:

#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

Danke, das habe ich erwartet: Sie sind explizit weggelassen. Hast du eine Idee über die Gründe? Würde zum Beispiel irgendetwas kaputt gehen oder zu kompliziert implementiert werden, wenn dieser bestimmte Satz weggelassen würde? – mindriot

+0

@mindriot Siehe bearbeiten. – Columbo

+0

Danke, das macht Sinn! – mindriot