Warum scheint niemand in C ++ Tupel zu verwenden, auch nicht die Boost Tuple Library noch die Standardbibliothek für TR1? Ich habe viel C ++ - Code gelesen und sehe sehr selten die Verwendung von Tupeln, aber ich sehe oft viele Stellen, an denen Tupel viele Probleme lösen würden (normalerweise werden mehrere Werte von Funktionen zurückgegeben).
Mit Tupeln kannst du alle möglichen coolen Dinge wie diese machen:
tie(a,b) = make_tuple(b,a); //swap a and b
Das ist sicherlich besser als das:
temp=a;
a=b;
b=temp;
Natürlich können Sie dies immer tun:
swap(a,b);
Aber was ist, wenn Sie drei Werte drehen möchten? Sie können dies mit Tupeln tun:
tie(a,b,c) = make_tuple(b,c,a);
Tupel machen es auch viel einfacher, mehrere Variablen von einer Funktion zurückzugeben, was wahrscheinlich ein viel häufigerer Fall ist als das Austauschen von Werten. Die Verwendung von Verweisen auf Rückgabewerte ist sicherlich nicht sehr elegant.
Gibt es große Nachteile von Tupeln, an die ich nicht denke? Wenn nicht, warum werden sie selten verwendet? Sind sie langsamer? Oder sind die Leute einfach nicht an sie gewöhnt? Ist es eine gute Idee, Tupel zu verwenden?
a = a ^ b; b = a ^ b; a = a ^ b;
Antworten:
Weil es noch nicht Standard ist. Alles, was nicht dem Standard entspricht, hat eine viel höhere Hürde. Pieces of Boost sind populär geworden, weil Programmierer nach ihnen verlangten. (hash_map fällt mir ein). Obwohl Tupel praktisch ist, ist es kein so überwältigender und klarer Gewinn, dass sich die Leute damit beschäftigen.
quelle
Eine zynische Antwort ist, dass viele Leute in C ++ programmieren, aber die übergeordneten Funktionen nicht verstehen und / oder verwenden. Manchmal liegt es daran, dass sie nicht erlaubt sind, aber viele versuchen es einfach nicht (oder verstehen es sogar nicht).
Als Beispiel ohne Boost: Wie viele Leute verwenden die Funktionen von
<algorithm>
?Mit anderen Worten, viele C ++ - Programmierer sind einfach C-Programmierer, die C ++ - Compiler verwenden, und vielleicht
std::vector
undstd::list
. Dies ist ein Grund, warum die Verwendung vonboost::tuple
nicht häufiger ist.quelle
Die C ++ - Tupelsyntax kann etwas ausführlicher sein, als die meisten Leute möchten.
Erwägen:
Wenn Sie also Tupel ausgiebig nutzen möchten, erhalten Sie entweder überall Tupel-Typedefs oder überall ärgerlich lange Typnamen. Ich mag Tupel. Ich benutze sie bei Bedarf. Normalerweise ist es jedoch auf einige Situationen beschränkt, z. B. einen N-Element-Index oder die Verwendung von Multimaps zum Binden der Bereichsiteratorpaare. Und es ist normalerweise in einem sehr begrenzten Umfang.
Im Vergleich zu etwas wie Haskell oder Python sieht alles sehr hässlich und hackig aus. Wenn C ++ 0x hier ankommt und wir die Schlüsselwort-Tupel "Auto" erhalten, werden sie viel attraktiver.
Die Nützlichkeit von Tupeln ist umgekehrt proportional zur Anzahl der Tastenanschläge, die zum Deklarieren, Packen und Entpacken erforderlich sind.
quelle
Für mich ist es Gewohnheit, zweifellos: Tupel lösen keine neuen Probleme für mich, nur ein paar, mit denen ich schon gut umgehen kann. Das Tauschen von Werten fühlt sich auf altmodische Weise immer noch einfacher an - und was noch wichtiger ist, ich denke nicht wirklich darüber nach, wie man "besser" tauscht. Es ist gut genug wie es ist.
Persönlich denke ich nicht, dass Tupel eine großartige Lösung für die Rückgabe mehrerer Werte sind - das klingt nach einem Job für
struct
s.quelle
Aber was ist, wenn Sie drei Werte drehen möchten?
OK, also mit 4 usw. Werten wird das n-Tupel schließlich weniger Code als n-1 Swaps. Und mit dem Standard-Swap werden 6 Zuweisungen anstelle der 4 ausgeführt, die Sie hätten, wenn Sie selbst eine Vorlage mit drei Zyklen implementiert hätten, obwohl ich hoffen würde, dass der Compiler dies für einfache Typen lösen würde.
Sie können Szenarien entwickeln, in denen Swaps unhandlich oder unangemessen sind, zum Beispiel:
ist etwas umständlich auszupacken.
Punkt ist jedoch, dass es bekannte Wege gibt, mit den häufigsten Situationen umzugehen, für die Tupel gut sind, und daher keine große Dringlichkeit, Tupel aufzunehmen. Wenn nichts anderes, bin ich nicht sicher, dass:
macht keine 6 Kopien, was es für einige Typen völlig ungeeignet macht (Sammlungen sind die offensichtlichsten). Fühlen Sie sich frei, mich davon zu überzeugen, dass Tupel eine gute Idee für "große" Typen sind, indem Sie sagen, dass dies nicht so ist :-)
Für die Rückgabe mehrerer Werte sind Tupel perfekt, wenn die Werte nicht kompatibel sind. Einige Leute mögen sie jedoch nicht, wenn der Aufrufer sie in die falsche Reihenfolge bringen kann. Einige Leute mögen mehrere Rückgabewerte überhaupt nicht und möchten ihre Verwendung nicht fördern, indem sie sie einfacher machen. Einige Leute bevorzugen nur benannte Strukturen für In- und Out-Parameter und konnten wahrscheinlich nicht mit einem Baseballschläger überredet werden, Tupel zu verwenden. Keine Berücksichtigung des Geschmacks.
quelle
Wie viele Leute betonten, sind Tupel einfach nicht so nützlich wie andere Funktionen.
Die tauschenden und rotierenden Gimmicks sind nur Gimmicks. Sie sind äußerst verwirrend für diejenigen, die sie noch nie gesehen haben, und da es so ziemlich jeder ist, sind diese Gimmicks nur eine schlechte Softwareentwicklungspraxis.
Das Zurückgeben mehrerer Werte mithilfe von Tupeln ist viel weniger selbstdokumentierend als die Alternativen - das Zurückgeben benannter Typen oder die Verwendung benannter Referenzen. Ohne diese Selbstdokumentation ist es leicht, die Reihenfolge der zurückgegebenen Werte zu verwechseln, wenn sie sich gegenseitig konvertieren lassen und nicht klüger sind.
quelle
Nicht jeder kann Boost verwenden, und TR1 ist noch nicht weit verbreitet.
quelle
Bei Verwendung von C ++ auf eingebetteten Systemen wird das Abrufen von Boost-Bibliotheken komplex. Sie koppeln miteinander, sodass die Bibliotheksgröße zunimmt. Sie geben Datenstrukturen zurück oder verwenden die Parameterübergabe anstelle von Tupeln. Bei der Rückgabe von Tupeln in Python ist die Datenstruktur in der Reihenfolge und Art der zurückgegebenen Werte nicht explizit.
quelle
Sie werden selten gesehen, weil gut gestalteter Code sie normalerweise nicht benötigt. In der Natur gibt es nicht viele Fälle, in denen die Verwendung einer anonymen Struktur der Verwendung einer benannten Struktur überlegen ist. Da alles, was ein Tupel wirklich darstellt, eine anonyme Struktur ist, passen die meisten Codierer in den meisten Situationen einfach zur Realität.
Angenommen, wir haben eine Funktion "f", bei der eine Tupelrückgabe sinnvoll sein könnte. In der Regel sind solche Funktionen so kompliziert, dass sie fehlschlagen können.
Wenn "f" fehlschlagen kann, benötigen Sie eine Statusrückgabe. Schließlich möchten Sie nicht, dass Anrufer jeden Parameter überprüfen müssen, um einen Fehler zu erkennen. "f" passt wahrscheinlich in das Muster:
Das ist nicht schön, aber schauen Sie, wie hässlich die Alternative ist. Beachten Sie, dass ich noch einen Statuswert benötige, der Code jedoch nicht mehr lesbar und nicht kürzer ist. Es ist wahrscheinlich auch langsamer, da mir die Kosten für 1 Kopie mit dem Tupel entstehen.
Ein weiterer bedeutender Nachteil ist hier verborgen: Mit "ReturnInts" kann ich die Rückgabe von "f" ändern, indem ich "ReturnInts" ändere, ohne die Schnittstelle von "f" zu ändern. Die Tupellösung bietet diese wichtige Funktion nicht, was sie zur schlechteren Antwort für jeden Bibliothekscode macht.
quelle
using std::tuple;
und einfach verwendentuple
.tuple
die Verwendung wird der Code weniger lesbar, nicht mehr. Der meiste Code enthält heutzutage eine sehr große Anzahl von Symbolen - das Sehenstd::tuple
macht dem Auge genau klar, was es ist.Natürlich können Tupel nützlich sein, aber wie bereits erwähnt, gibt es ein bisschen Overhead und ein oder zwei Hürden, durch die man springen muss, bevor man sie überhaupt wirklich benutzen kann.
Wenn Ihr Programm ständig Orte findet, an denen Sie mehrere Werte zurückgeben oder mehrere Werte austauschen müssen, lohnt es sich möglicherweise, die Tupelroute zu wählen. Andernfalls ist es manchmal einfacher, die Dinge auf klassische Weise zu erledigen.
Im Allgemeinen ist Boost noch nicht bei allen installiert, und ich würde sicherlich nicht die Mühe machen, es herunterzuladen und meine Include-Verzeichnisse so zu konfigurieren, dass es nur für seine Tupel-Funktionen damit funktioniert. Ich denke, Sie werden feststellen, dass Leute, die Boost bereits verwenden, eher Tupelverwendungen in ihren Programmen finden als Nicht-Boost-Benutzer, und Migranten aus anderen Sprachen (Python kommt in den Sinn) sind eher verärgert über das Fehlen von Tupeln in C ++ als Methoden zum Hinzufügen von Tupelunterstützung zu untersuchen.
quelle
Da ein Datenspeicher
std::tuple
die schlechtesten Eigenschaften sowohl einesstruct
als auch eines Arrays aufweist; Jeder Zugriff basiert auf der n-ten Position, aber man kanntuple
einefor
Schleife nicht durchlaufen .Wenn die Elemente in
tuple
konzeptionell ein Array sind, verwende ich ein Array. Wenn die Elemente konzeptionell kein Array sind, ist eine Struktur (die Elemente benannt hat) besser zu warten. (a.lastname
ist erklärender alsstd::get<1>(a)
).Damit bleibt die vom OP erwähnte Transformation der einzige praktikable Anwendungsfall für Tupel.
quelle
Ich habe das Gefühl, dass viele Boost.Any und Boost.Variant (mit etwas Technik) anstelle von Boost.Tuple verwenden.
quelle