|
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 05.04.2007, 23:18 Titel: [template c++] mehrdeutigkeit ausschließen (?) |
|
|
Hi,
ich habe ein leicht verzwicktes Problem mit templates, wozu mir dummerweise gerade die Lösung nicht einfällt; wäre sehr nett wenn mir einer auf die Sprünge helfen kann (gelegenheit zu zeigen, wer auch so angetan ist von den Template-Wundern, wie auch Kampfhund hier ein paar gezeigt hat ):
Stark vereinfacht an einem kleinen Beispiel sieht das so aus (erläuterungen zu dem Code schreib ich drunter): CPP: | struct Foo1
{
template<typename T> friend T operator<< ( T t, Foo1 f1 )
{
return t;
}
};
struct Foo2
{
template<typename T> T operator<< ( T t )
{
return t;
}
}; |
Also: ich habe wie man sieht zwei Klassen (hier der Einfachheit halber structs). Die Funktionsrümpfe in beiden sind in dem Bsp. hier zwar sinnlos, aber ich hab versucht es so klein wie möglich zu halten. In Foo1 ist ein globaler operator<< definiert, als friend da er in meiner realen Klasse als Ausgabeoperator für streams gilt und private-data-access haben muss. In Foo2 ist ein operator<< als Member deklariert, der in der realen Klasse ein ausgabe-operator von einem Stream ist: das sieht dann so aus, dass Foo2 mein stream ist und Foo1 ein Objekt das auf dem Stream ausgegeben wird, sprich: CPP: | Foo1 f1;
Foo2 f2;
// ...
f2 << f1; | Kommen wir zum Kern der Sache:
Ich möchte, dass hier der globale operator<< (T, Foo1) aufgerufen wird mit T = Foo2; und nicht der aus Foo2, also nicht Foo2::operator<< (Foo1) mit T = Foo1. Ich möchte also explizit ausschließen, dass der T von Foo2::operator<< den Typ Foo1 annehmen kann.
Das enable_if-Idiom (eine abgeschwächte Art davon) hab ich schon versucht, nur leider kontrolliert der Compiler (der bei msvc++05 express) diese Sache erst nach der Mehrdeutigkeit. Mein Ansatz dabei war folgender, falls das hilfreich sein könnte: CPP: | template<typename T> struct enable_type
{ typedef int tt; };
template<> struct enable_type<Foo1>
{ typedef void tt; };
// und dann in dem operator, für den Foo1 ausgeschlossen werden soll:
struct Foo2
{
template<typename T> T operator<< ( T t )
{
enable_type<T>::tt _Foo1_as_T_not_allowed = 0;
return t;
}
};
| Für T = Foo1 würde da dann nämlich bekanntlicherweisen CPP: | void _Foo1_as_T_not_allowed = 0; | stehen, was den Fehler erzeugen würde...
So, ziemlich viel, aber ich hoffe ich konnte mein Problem hinreichend gut beschreiben. Wäre sehr nett, wenn einer, der Ahnung von diesen template-geschichten hat, mir dabei helfen könnte. Ich brauch ja im Endeffekt nur etwas in dem 2. op., dass dem Compiler sagt: "T kann hier nicht mit Foo1 instanziert werden"
Gruß DXer, und vielen Dank im Voraus! |
|
Nach oben |
|
|
David Super JLI'ler
Alter: 39 Anmeldedatum: 13.10.2005 Beiträge: 315
Medaillen: Keine
|
Verfasst am: 06.04.2007, 00:23 Titel: |
|
|
Der Operator<< wird allerdings zwangsläufig so implementiert:
CPP: | struct Foo1
{
template< typename T >
friend Foo1& operator<<( Foo1& stream, T& obj )
{
return stream;
}
};
|
Ansonsten sind Späße wie: x << y << z << foo << bar nich möglich.
Insofern erübrigt sich die Frage egtl! |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 06.04.2007, 11:49 Titel: |
|
|
nein, Foo1 ist nicht der Stream sondern ein object. Foo2 ist der stream. das is ja gerade der Unterschied der beiden: In dem globalen operator in Foo1 soll der Streamtyp T der left-type sein, und Foo1 der right-type, sodass dieser globale Operator in allen folgenden Fällen aufgerufen wird: CPP: | // mehrere streams:
std::ofstream os;
std::wofstream ws;
std::wostringstream wss;
std::stringstream ss;
//...
Foo1 f;
// ...
os << f; // aufgelöst zu: ::operator<< <std::ofstream> (os, f);
ws << f; // aufgelöst zu: ::operator<< <std::wofstream> (ws, f);
wss << f; // aufgelöst zu: ::operator<< <std::wostringstream> (wss, f);
ss << f; // aufgelöst zu: ::operator<< <std::stringstream> (ss, f); |
Das ist praktisch eine weitere Überladung der globalend std::operatoren folgenden Ursprungs (also l = stream, r = object) CPP: | template<class _Traits> inline
basic_ostream<char, _Traits>& __CLRCALL_OR_CDECL operator<<(
basic_ostream<char, _Traits>& _Ostr, char _Ch) |
Im Vergleich mit dem operator von Foo2 hingegen sehen die beiden mehrdeutigen Aufrufe so aus: CPP: | Foo1 obj;
Foo2 stream;
//...
stream << obj;
// mögliche Aufrufe (mehrdeutigkeiten):
// 1.
::operator<< <Foo2> (stream, obj);
// 2.
stream.operator<< <Foo1> (obj); |
ich möchte dem Compiler dabei irgendwie klar machen, dass es nur die 1. möglichkeit geben kann. Es soll für ihn also keine Möglichkeit geben, Foo2::operator<< mit Foo1 instanzieren zu können, sprich: Foo1 soll nicht in der Liste der möglichen template-Parameter von Foo2::operator<< stehen, sodass nur der Aufruf durch den globalen operator infrage käme.
Wenn es dabei keine Möglichkeit gibt, werde ich wohl oder übel den template-operator in Foo1 zum normalen operator machen müssen und ihn dann für alle möglichen stream objekte überladen, also auch für Foo2.
Gruß DXer |
|
Nach oben |
|
|
David Super JLI'ler
Alter: 39 Anmeldedatum: 13.10.2005 Beiträge: 315
Medaillen: Keine
|
Verfasst am: 06.04.2007, 15:03 Titel: |
|
|
Na viel Spaß! In deinem Fall ist der Operator << eben doppelt implementiert. Nimm eine Version raus und gut is. Der Ansatz mit dem enable_if Idiom kann nicht funktionieren, da das nichts daran ändert das der Operator zweimal implementiert wurde.
Wie du dem Compiler klar machst welche Funktion aufgerufen werden soll hast du ja bereits selbst gesagt:
CPP: | operator<< < Foo2 >( f2, f1 );
|
|
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 06.04.2007, 15:44 Titel: |
|
|
nun gut, ich habe das jetzt folgendermaßen gemacht:
CPP: | struct Foo2;
struct Foo1
{
inline friend Foo2& operator<< (Foo2& stream, Foo1& obj)
{
return obj.writeToStream(stream);
}
inline friend std::ostream& operator<< (std::ostream& stream, Foo1& obj)
{
return obj.writeToStream(stream);
}
inline friend std::ofstream& operator<< (std::ofstream& stream, Foo1& obj)
{
return obj.writeToStream(stream);
}
inline friend std::ostringstream& operator<< (std::ostringstream& stream, Foo1& obj)
{
return obj.writeToStream(stream);
}
// usw. für andere streams
private:
template<typename T_Stream> T_Stream& writeToStream(T_Stream& str)
{
// code zur speziellen ausgabe auf stream...
return str;
}
};
|
Der Hintergrund dafür ist, dass Foo2::operator<< ein (intern formatierter) standard-ausgabeoperator für Foo2-streams darstellt. Für Foo1 möchte ich aber eine ganz spezielle Ausgabe haben, da es ja 1. kein integrierter Typ ist und 2. eigene Variablen hat, die ausgegeben werden sollen. Foo2::operator<< kennt von Natur aus keine Foo1-Objekte und wüsste daher auch nicht, wie es mit solchen umgehen müsste. Ein CPP: | Foo2::operator<< <Foo1> (f1); | verursacht somit auch explizit errors ala "... kann nicht in int konvertiert werden" o.Ä. Trotzdem hat der Compiler es als Mehrdeutigkeit angesehen, obwohl klar war, dass es nur die eine Möglichkeit (nämlich die über den globalen Operator) geben kann.
Trotzdem vielen Dank für deine Geduld und Hilfe, david!
Gruß DXer
Gruß DXer |
|
Nach oben |
|
|
xardias JLI Master
Alter: 38 Anmeldedatum: 28.12.2003 Beiträge: 804 Wohnort: Palo Alto, CA Medaillen: Keine
|
Verfasst am: 22.05.2007, 12:35 Titel: |
|
|
afaik muss man beim aufrufen von templateisierten memberfunktionen eine spezielle syntax verwenden welche von kaum einem compiler unterstützt wird.
ich hab den stroustrup nicht zur hand aber um
Code: | class A
{
template<class > test(T blub){...}
} |
aufzurufen muss man afaik folgende syntax verwenden:
Code: | A a;
a.template<int> test(test);
|
wie das dann bei operatoren funktionieren soll.. keine ahnung ob das überhaupt möglich ist.. zumal die syntax von kaum einem compiler unterstützt wird und somit (ich spreche für gcc) das ganze überhaupt nicht möglich ist. |
|
Nach oben |
|
|
David Super JLI'ler
Alter: 39 Anmeldedatum: 13.10.2005 Beiträge: 315
Medaillen: Keine
|
Verfasst am: 22.05.2007, 15:17 Titel: |
|
|
Wie wärs mit:
CPP: | struct foo
{
template< typename T > void bla( const T& )
{}
template< typename T > foo& operator+=( const T& )
{
return *this;
}
};
int main()
{
foo x;
x.bla( 100 ); // Erkennt automatisch int für den Templateparameter
x.bla< float >( 100 ); // Templateparameter statisch angeben
x.operator+=< float >( 100.0f ); // Operator += mit statischen Templateparameter
std::cin.get();
} |
Der Aufruf von "Operator" und Methode funktioniert nämlich ganz genau gleich weil du nicht den Operator aufrust sondern die Methode operator+=( const T& ).
Komisch dass das so wenig Leute kapieren. Man übschreibt in C++ niemals irgendeinen Operator sondern immer nur die Methode/Funktion die vom Operator aufgerufen wird.
grüße |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 22.05.2007, 16:45 Titel: |
|
|
xardias hat Folgendes geschrieben: |
Code: | class A
{
template<class > test(T blub){...}
} |
aufzurufen muss man afaik folgende syntax verwenden:
Code: | A a;
a.template<int> test(test);
|
|
du hast recht, es gibt diese syntax, und in boost kommt sie sogar unter manchen Vorraussetzungen zum Einsatz, aber es geht auch anders: (und dies wird im Grunde von fast allen Compilern unterstützt):
CPP: | A a;
a.test<int> test(test); | Aber eigtl ist selbst das nicht nötig, da wenn die Funktion einen Parameter des Typs T erwartet und auch bekommt, dann ermittelt der Compiler (und das ist nach ISO C++ Standard vorausgesetzt) den Typ selber, wie auch david schon gezeigt hat:
BTW: Ohne dich jetz ärgern zu wollen, da ist so ein... naja... inneres Verlangen in mir, das mich zwingt, deine Template-Member-Definition richtig zu stellen, sorry CPP: | class A
{
template<class T> T test(T blub){...} // Template-typname, Rückgabewert
}; // <- |
Gruß DXer |
|
Nach oben |
|
|
xardias JLI Master
Alter: 38 Anmeldedatum: 28.12.2003 Beiträge: 804 Wohnort: Palo Alto, CA Medaillen: Keine
|
Verfasst am: 23.05.2007, 14:38 Titel: |
|
|
sorry, saß im info praktikum war deprimiert wegen der beknackten aufgaben und mir war langweilig. da passieren so fehler |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|