argument-dependent lookup + namespace
-
Darf das argument-dependent lookup sich auch über namespaces hinwegsetzen?
bsp.:
#include <string> int main() { std::string a; std::string b; swap(a, b); }kompiliert unter msvc
das es am adl liegt, schließe ich daraus:
#include <string> int main() { int a = 0; int b = 1; swap(a, b); }->
MSVC 9 schrieb:
error C3861: 'swap': identifier not found
Für spezialisierte fkt scheint es also auch ohne namespace zu gehen - ist das richtig oder ist das ein feature/bug des msvc?
bb
-
unskilled schrieb:
Darf das argument-dependent lookup sich auch über namespaces hinwegsetzen?
Das ist ja ein Feature von ADL.

Es wird unter anderem auch im Namensraum der Parametertypen gesucht, ob die entsprechende Funktion vorhanden ist.
-
ADL setzt sich nicht über einen
namespacehinweg, sondern es garantiert, dass auch imnamespaceder Argumente gesucht wird.Im ersten Beispiel wird also auch im
namespace stdgesucht, weil von dort die Klassestringherkommt.
Im zweiten Beispiel gehören die Typen aber keinemnamespacean. Also wird nur global gesucht und nichts gefunden.Grüssli
-
ty - wieder was dazu gelernt ^^
bb
-
Ein dazu passender Link: Namespaces and the Interface Principle
Wenn man
swapnoch "generisch" benutzen will, sollte man das dann so machen:template<typename T> void rotate_left(T & a, T & b, T & c) { using std::swap; swap(a,b); swap(b,c); }Beachte: Nur ein unqualifiziertes
swaperlaubt ADL. So kann zB das folgendeswapnoch fürT=my_typeüber ADL gefunden werden:namespace my_namespace { class my_type { int * p; public: explicit my_type(int*p); my_type(my_type const&); my_type& operator=(my_type const&); ~my_type(); void swap(my_type & with); }; inline void swap(my_type & a, my_type & b) { a.swap(b); } }Trotzdem wird das Template von oben noch für fundamentale Typen funktionieren, weil
std::swapperusing-Deklaration in den Geltungsbereich geholt wurde.Gruß,
SP
-
Sebastian Pizer schrieb:
Ein dazu passender Link: Namespaces and the Interface Principle
ty
Sebastian Pizer schrieb:
Wenn man
swapnoch "generisch" benutzen will, sollte man das dann so machen:template<typename T> void rotate_left(T & a, T & b, T & c) { using std::swap; swap(a,b); swap(b,c); }Hab ich zwar auch schon immer so gemacht aber jetzt eben erst so richtig verstanden, warum man es wirklich so macht - davor dachte ich halt immer, dass ein mehr spezialisiertes swap nur gefunden werden kann, wenn es im globalen(oder in diesem - also im aktuellen) scope liegt...
also danke noch mal : >
bb
-
Hm, gerade noch eine passende Frage dazu:
Überladet ihr im Allgemeinen ein globales (bzw. im eigenen Namensraum)
swap()für euren Typen und hofft darauf, dass der Anwender jeweilsusing std::swap; swap(a,b);schreibt, oder spezialisiert ihr dasstd::swap-Template und spekuliert, dass der Anwenderstd::angibt?
Welche Varianten würdet ihr hier wählen?
class MyClass {}; // 1) void swap(MyClass&, MyClass&) { std::cout << "swap" << std::endl; } // 2) template <> void std::swap(MyClass&, MyClass&) { std::cout << "swap<MyClass>" << std::endl; } int main() { MyClass x, y; // A) swap(x, y); // B) std::swap(x, y); // C) using std::swap; swap(x, y); }Ist das
std::bei 2) so eigentlich okay und gibt es einen Unterschied zu einem umschliessendennamespace std {}?
-
Nexus schrieb:
Hm, gerade noch eine passende Frage dazu:
Überladet ihr im Allgemeinen ein globales (bzw. im eigenen Namensraum)
swap()für euren Typen und hofft darauf, dass der Anwender jeweilsusing std::swap; swap(a,b);schreibt, oder spezialisiert ihr dasstd::swap-Template und spekuliert, dass der Anwenderstd::angibt?
Ich spezialisiere std::swap so dass es meine eigene swap-Routine aufruft. Damit sind alle Möglichkeiten abgedeckt. Denn leider schreibt der Anwender nicht immer using std::swap sondern ruf std::swap direkt explizit auf (selbst einige STL-Implementierungen machen das leider).
-
Ah danke, ja das scheint noch Sinn zu machen. Allerdings ist es etwas mühsam, jeweils drei Funktionen (inklusive Memberfunktion) bereitzustellen.
Hm... Irgendwie gerate ich da in Versuchung eines bösen Makro-Hacks... :p
Aber vorläufig werde ich es wohl direkt mit deiner Methode versuchen, allzu schlimm sind die paar Zeilen Code ja nicht.
-
swapim eigenen Namespace selbst zu definieren, ist nur dann nötig, wenn man Template-Klassen swappen will.Es ist nämlich erlaubt
std::swapzu spezialisieren, aber nicht zusätzliche Overloads zu erzeugen.Da Funktions-Templates nicht partiell spezialisiert werden können, muss für Dinge wie
swap(boost::shared_ptr<T>&, boost::shared_ptr<T>&)ein Overload her.
Und da man diesen Overload wie gesagt (leider) nicht instd::machen darf, muss er in den Namespace dershared_ptr<>definiert, damit er über ADL gefunden wird. D.h. in diesem Fall "boost".
(Technisch geht es natürlichstd::swapzu überladen, aber es ist halt nicht "erlaubt")Daher sollte man IMO auch die
using std::swap; swap(a, b);Variante verwenden, wenn man selbst "swappenden" Code schreibt.
-
Für "normale" Klassen (also keine Klassentemplates) kann man beides machen: eine freie swap-Funktion im gleichen Namensraum wie die Klasse plus eine Spezialisierung für std::swap. Da es keine partiellen Spezialisierungen für Funktions-Templates gibt, geht das mit Klassentemplates nicht mehr.
Das Spezialisieren von Funktionstemplates ist etwas, was ich vermeide. Überladen finde ich da sinvoller. Aber das ist ja im std-Namensraum illegal.
Mit dem, was die Leute sich für C++0x überlegt haben, wird auch eine einzige swap-Funktion im eigenen Namensraum reichen.
Ist das
std::bei 2) so eigentlich okay und gibt es einen Unterschied zu einem umschliessendennamespace std {}?Ich glaube, das ist nicht erlaubt.
Gruß,
SP
-
Sebastian Pizer schrieb:
Mit dem, was die Leute sich für C++0x überlegt haben, wird auch eine einzige swap-Funktion im eigenen Namensraum reichen.
Das würde es momentan eigentlich auch, wenn sich der Aufrufer etwas achten würde. Oder was meinst du konkret?
Sebastian Pizer schrieb:
Ich glaube, das ist nicht erlaubt.
Das Gefühl habe ich irgendwie auch. Ich habe diese Schreibweise bisher gar noch nie verwendet, aber mal spontan ausprobiert, und es hat normal kompiliert (MSVC++). Weiss jemand Genaueres darüber?
-
Nexus schrieb:
Irgendwie gerate ich da in Versuchung eines bösen Makro-Hacks... :p
Nur halb böse
friend-injection machts doch auch, zumindest für die ADL-Swap-Funktion. Siehe boost::operators (wie dies machen hab ich im dritten Operator-Artikel erklärt):namespace SomeWhere { template <class T> struct Swappable { friend void swap(T& lhs, T& rhs) { lhs.swap(rhs); } }; } //end ns SomeWhere namespace SomeWhereElse { class Foo : private SomeWhere::Swappable<Foo> { public: void swap(Foo& rhs); } } int main() { using SomeWhereElse::Foo; Foo a, b; swap(a, b); }Leider gehts für std::swap nicht: man darf keine qualifizierten Funktionen als friends definieren, erst recht dürfen keine Template-Spezialisierungen während einer friend-Deklaration definiert werden. Leider kann man auch keine partielle Spezialisierung eines Funktionstemplates machen, schön wäre folgendes:
template <class T> void ::std::swap<SomeWhere::Swappable<T> >(Swappable<T>& lhs, Swappable<T>& rhs) { (static_cast<T&>(lhs)).swap(static_cast<T&>(rhs)); }Das hier wäre eine Überladung, was die verbotene Einführung einer zusätzlichen Funktion in den Namespace std wäre.
-
Nexus schrieb:
Sebastian Pizer schrieb:
Ich glaube, das ist nicht erlaubt.
Das Gefühl habe ich irgendwie auch. Ich habe diese Schreibweise bisher gar noch nie verwendet, aber mal spontan ausprobiert, und es hat normal kompiliert (MSVC++). Weiss jemand Genaueres darüber?
Um euren Glauben und euer Gefühl zu bestätigen der entsprechende Ausschnitt aus dem Standard:
ISO/IEC 14882 §17.4.3/2 schrieb:
An explicit specialization shall be declared in the namespace of which the template is a member [...] Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later in the namespace in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.
-
hustbaer, auch dir danke für die Erklärungen, ich hab deinen Post vorhin gar nicht gesehen.
hustbaer schrieb:
Daher sollte man IMO auch die
using std::swap; swap(a, b);Variante verwenden, wenn man selbst "swappenden" Code schreibt.Würde sich dann evtl. sowas empfehlen?
template <typename T> void adl_swap(T& lhs, T& rhs) { using std::swap; swap(lhs, rhs); }pumuckl schrieb:
Nur halb böse
friend-injection machts doch auch, zumindest für die ADL-Swap-Funktion.Stimmt. Ich hab mich nämlich zuerst auch gefragt, wie die Boost.Operators-Entwickler freie Funktionen über Basisklassen bereitstellen wollen...

Recht praktisch, die Technik.pumuckl schrieb:
Leider gehts für std::swap nicht: man darf keine qualifizierten Funktionen als friends definieren.
Sorry, hier bin ich nicht ganz nachgekommen, inwiefern meinst du "qualifiziert"? Durch den Namensraum?
pumuckl schrieb:
Leider kann man auch keine partielle Spezialisierung eines Funktionstemplates machen, schön wäre folgendes
Das sollte doch auch mit dem neuen Standard kommen, wenn ich mich nicht täusche...
pumuckl schrieb:
Um euren Glauben und euer Gefühl zu bestätigen der entsprechende Ausschnitt aus dem Standard:
ISO/IEC 14882 §17.4.3/2 schrieb:
An explicit specialization shall be declared in the namespace of which the template is a member [...] Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later in the namespace in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.
Wenn ich das richtig verstehe, führt das
std::beivoid std::swapalso gar nicht zur Einbettung in den Namensraum. Das habe ich bisher falsch interpretiert. Wozu wird diese Schreibweise (im Gegensatz zum konventionellen, umschliessenden Namensraum) verwendet?
-
Nexus schrieb:
pumuckl schrieb:
Leider gehts für std::swap nicht: man darf keine qualifizierten Funktionen als friends definieren.
Sorry, hier bin ich nicht ganz nachgekommen, inwiefern meinst du "qualifiziert"? Durch den Namensraum?
Ist im Grunde das Selbe wie der Auszug aus dem Standard den ich gepostet habe. Du kannst per friend-injection keine Funktion in einen anderen Namespace definieren. Oder andersrum: wenn du einen qualifizierten (also im Grunde alles mit :: drin) als friend deklarierst, kann es eben nur eine Deklaration und keine Definition sein. Definitionen beziehen sich immr auf den umgebenden Namespace, alles andere kann nur dort definiert werden wo es wirklich hingehört.
pumuckl schrieb:
Um euren Glauben und euer Gefühl zu bestätigen der entsprechende Ausschnitt aus dem Standard:
ISO/IEC 14882 §17.4.3/2 schrieb:
An explicit specialization shall be declared in the namespace of which the template is a member [...] Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later in the namespace in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.
Wenn ich das richtig verstehe, führt das
std::beivoid std::swapalso gar nicht zur Einbettung in den Namensraum. Das habe ich bisher falsch interpretiert. Wozu wird diese Schreibweise (im Gegensatz zum konventionellen, umschliessenden Namensraum) verwendet?[/quote] Die Schreibweise kann wie der Text besagt dazu verwendet werden, um im globalen namespace eine Spezialisierung von std::swap zu definieren - aber nur wenn sie orher innerhalb von namespace std deklariert wurde. MSVC scheint da etwas freizügiger zu sein, da er die Definition auch in anderen Namespaces und ohne vorherige korrekt plazierte Deklaration schluckt.namespace std { template<> void swap<Foo>(Foo&, Foo&); template<> void swap<Bla>(Bla&, Bla&); } template<> void std::swap<Foo>(Foo&, Foo&) {} //OK template<> void std::swap<Bar>(Bar&, Bar&) {} //FEHLER - wurde nicht in std deklariert namespace OuterSpace { template<> void ::std::swap<Bla>(Bla&, Bla&) {} //FEHLER - OuterSpace ist kein umgebender namespace von std}
-
pumuckl schrieb:
Leider kann man auch keine partielle Spezialisierung eines Funktionstemplates machen, schön wäre folgendes:
template <class T> void swap<SomeWhere::Swappable<T> >(Swappable<T>& lhs, Swappable<T>& rhs) { (static_cast<T&>(lhs)).swap(static_cast<T&>(rhs)); }Das hier wäre eine Überladung, was die verbotene Einführung einer zusätzlichen Funktion in den Namespace std wäre.
Die <>-Klammern hinter swap deuten auf eine Spezialisierung hin, die in diesem Fall partiell und damit illegal ist. Ein neues Funktionstemplate müsste man ohne diese Klammern schreiben.
Ich bin kein Freund von Funktions-Template-Spezialisierungen. Die werden zB nicht bei der Auflösung von Überladungen beachtet, auch wenn sie "spezieller" sind. Beispiel:
#include <iostream> template<typename U> struct s{}; template<typename T> void foo(T x) { std::cout << "1\n"; } template<> void foo<s<int> >(s<int> x) { std::cout << "2\n"; } template<typename U> void foo(s<U> x) { std::cout << "3\n"; } int main() { foo(s<int>()); }Hier sind 2 Funktions-Templates überladen worden, wobei eines davon für s<int> spezialisiert wurde. Ist die Ausgabe des Programms 1, 2 oder 3? Obwohl eigentlich die Spezialisierung des ersten Templates am "besten passen" sollte, richtet sich die "overload resolution" nicht danach, was es alles so für Spezialisierungen gibt. Die Auflösung wird in diesem Fall durch die "partial ordering"-Regeln bestimmt, die sagen, dass das zweite Funktionstemplate
template<typename U> void foo(s<U>)spezieller als das erste Funktionstemplatetemplate<typename T> void foo(T)ist. Dementsprechend ist die Ausgabe des Programms "3".Nexus schrieb:
Sebastian Pizer schrieb:
Mit dem, was die Leute sich für C++0x überlegt haben, wird auch eine einzige swap-Funktion im eigenen Namensraum reichen.
Das würde es momentan eigentlich auch, wenn sich der Aufrufer etwas achten würde. Oder was meinst du konkret?
Stimmt. Man wird auch immer noch nicht davon abgehalten, ADL explizit durch Qualifizierungen auszuschalten. Mit Concepts ist es aber wahrscheinlich schwieriger, in der Hinsicht etwas falsch zu machen:
#include <concepts> // std::Swappable #include <algorithm> // std::swap template<typename T> requires std::Swappable<T> void rotate_left(T & a, T & b, T & c) { swap(a,b); swap(b,c); }Das wird auch für fundamentale Typen funktionieren, ohne, dass ich "using std::swap;" schreiben muss. Man sollte nur nicht vergessen <algorithm> einzufügen.
Gruß,
SP
-
Danke für die Antworten. Concepts werden wahrscheinlich auch sonst einiges zur Schnittstellenvereinheitlichung beitragen. Aber für die nächste Zeit scheint mir pumuckls Vorschlag noch recht flexibel, die automatische Codegenerierung durch Basisklassen ist wirklich nützlich.
Aber so nebenbei: Wird das wirklich
std::Swappableheissen oder war das mehr ein Beispiel? Die Grosschreibung von Bezeichnern im Standard erstaunt mich drum etwas...
-
Nexus schrieb:
Aber so nebenbei: Wird das wirklich
std::Swappableheissen oder war das mehr ein Beispiel? Die Grosschreibung von Bezeichnern im Standard erstaunt mich drum etwas...
Es sieht ganz so aus. std::Swappable gibt es wirklich im aktuellen Entwurf. Ich hatte es extra nachgeguckt (Kapitel 20.2).