Ich arbeite an einer großen Softwareanwendung, die auf mehreren Plattformen ausgeführt werden muss. Einige dieser Plattformen unterstützen einige Funktionen von C ++ 11 (z. B. MSVS 2010) und andere nicht (z. B. GCC 4.3.x). Ich gehe davon aus, dass diese Situation mehrere Jahre anhält (meine beste Schätzung: 3-5 Jahre).
Vor diesem Hintergrund möchte ich eine Kompatibilitätsschnittstelle einrichten, mit der (soweit möglich) mit minimalem Wartungsaufwand C ++ 11-Code geschrieben werden kann, der auch mit älteren Compilern kompiliert werden kann. Insgesamt besteht das Ziel darin, # ifdefs so weit wie möglich zu minimieren, während die grundlegenden C ++ 11-Syntax / -Funktionen auf den Plattformen, die sie unterstützen, weiterhin aktiviert werden, und die Emulation auf den Plattformen bereitzustellen, die dies nicht tun.
Beginnen wir mit std :: move (). Der naheliegendste Weg, um Kompatibilität zu erreichen, wäre, so etwas in eine gemeinsame Header-Datei zu schreiben:
#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
template <typename T> inline T& move(T& v) { return v; }
template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)
Dies ermöglicht es den Menschen, Dinge wie zu schreiben
std::vector<Thing> x = std::move(y);
... ungestraft. In C ++ 11 macht es, was es will, und in C ++ 03 macht es das Beste, was es kann. Wenn wir endlich den letzten C ++ 03-Compiler löschen, kann dieser Code unverändert bleiben.
Nach dem Standard ist es jedoch unzulässig, neue Symbole in den std
Namespace einzufügen. Das ist die Theorie. Meine Frage ist: Gibt es in der Praxis einen Schaden, wenn Sie dies tun, um die Vorwärtskompatibilität zu erreichen?
Antworten:
Ich habe lange daran gearbeitet, die Vorwärts- und Rückwärtskompatibilität in meinen C ++ - Programmen beizubehalten, bis ich schließlich ein Bibliothekstoolkit daraus erstellen musste , das
ich für die Veröffentlichung vorbereite. Eswurde bereits veröffentlicht. Im Allgemeinen, solange Sie akzeptieren, dass Sie keine "perfekte" Aufwärtskompatibilität erhalten, und zwar weder in Funktionen (einige Dinge können nur nicht in die Zukunft emuliert werden) noch in der Syntax (Sie müssen wahrscheinlich Makros oder alternative Namespaces für verwenden) einige Dinge), dann sind Sie fertig.Es gibt viele Funktionen, die in C ++ 03 in einer Stufe emuliert werden können, die für den praktischen Gebrauch ausreicht - und das ohne den ganzen Aufwand, der damit verbunden ist, z. B .: Boost. Selbst der C ++ - Standardvorschlag für
nullptr
schlägt einen C ++ 03-Backport vor. Und dann gibt es zum Beispiel TR1 für alles, was mit C ++ 11 zu tun hat, aber wir haben schon seit Jahren Previews. Nicht nur das, einige C ++ 14- Features wie Assert-Varianten, transparente Funktoren undoptional
lassen sich in C ++ 03 implementieren!Die einzigen zwei Dinge, von denen ich weiß, dass sie nicht unbedingt zurückportiert werden können, sind constexpr und variadic templates.
In Bezug auf das Hinzufügen von Inhalten zum Namespace
std
bin ich der Ansicht, dass es überhaupt keine Rolle spielt . Denken Sie an Boost, eine der wichtigsten und relevantesten C ++ - Bibliotheken, und deren Implementierung von TR1: Boost.Tr1. Wenn Sie C ++ verbessern möchten, machen Sie es vorwärtskompatibel mit C ++ 11, und Sie wandeln es per Definition in etwas um, das nicht C ++ 03 ist. Blockieren Sie sich also über einen Standard, den Sie vermeiden oder den Sie ohnehin zurücklassen möchten Einfach gesagt, kontraproduktiv. Puristen werden sich beschweren, aber per definitionem braucht man sich nicht um sie zu kümmern.Nur weil Sie dem (03) -Standard nicht folgen, heißt das natürlich nicht, dass Sie es nicht versuchen können, oder dass Sie es fröhlich umgehen werden , wenn Sie es brechen. Das ist nicht der Punkt. Solange Sie sehr sorgfältig kontrollieren, was dem
std
Namespace hinzugefügt wird , und eine Kontrolle über die Umgebungen haben, in denen Ihre Software verwendet wird (z. B. Testen!), Sollte es überhaupt keinen unverträglichen Schaden geben. Wenn möglich, definieren Sie alles in einem separaten Namespace und fügen Sie nurusing
Direktiven zum Namespace hinzu,std
damit Sie dort nichts hinzufügen, was "unbedingt" erforderlich ist. Was, IINM, ist mehr oder weniger das, was Boost.TR1 tut.Update (2013) : Hier finden Sie eine Liste der C ++ 11- und C ++ 14-Funktionen sowie den Grad der Portabilität der ursprünglichen Frage und einiger Kommentare, die ich aufgrund mangelnder Repräsentanz nicht ergänzen kann zu C ++ 03:
nullptr
: Vollständig umsetzbar angesichts des Rückstands des offiziellen Ausschusses; Sie müssen wahrscheinlich auch einige type_traits-Spezialisierungen angeben, damit es als "nativer" Typ erkannt wird.forward_list
: vollständig implementierbar, obwohl die Allokatorunterstützung davon abhängt, was Ihre Tr1-Implementierung bieten kann.vector<int> v = {1, 2, 3, 4};
: vollständig implementierbar, wenn auch wortreicher als man möchte.static_assert
: Nahezu vollständig implementierbar, wenn es als Makro implementiert wird (Sie müssen nur mit Kommas vorsichtig sein).unique_ptr
: nahezu vollständig implementierbar, aber Sie benötigen auch Unterstützung beim Aufrufen von Code (zum Speichern in Containern usw.); siehe unten.static_cast<>
Sprachfunktionen ist jedoch nahezu unmöglich.noexcept
: hängt von den Funktionen Ihres Compilers ab.auto
Semantik unddecltype
: hängt von den Funktionen Ihres Compilers ab - z__typeof__
.int16_t
, usw.): hängt von den Funktionen Ihres Compilers ab - oder Sie können an die Portable-Datei stdint.h delegieren.::type
immer in Vorlagen hattenconstexpr
: Meines Wissens nach nicht implementierbar.dynarray
: vollständig implementierbar.optional<>
: Nahezu vollständig implementierbar, solange Ihr C ++ 03-Compiler Ausrichtungs-Setups unterstützt.std::less<void>
damit es funktioniert.assure
): vollständig implementierbar, wenn Sie Asserts möchten, nahezu vollständig implementierbar, wenn Sie stattdessen Throws aktivieren möchten.(Haftungsausschluss: Mehrere dieser Funktionen sind in meiner oben verlinkten C ++ - Backports-Bibliothek implementiert, sodass ich denke, dass ich weiß, wovon ich spreche, wenn ich "vollständig" oder "fast vollständig" sage.)
quelle
Das ist grundsätzlich unmöglich. Überlegen Sie
std::unique_ptr<Thing>
. Wenn es möglich wäre, R-Wert-Referenzen als Bibliothek zu emulieren, wäre dies keine Sprachfunktion.quelle
std::unique_ptr
dort definiert werden sollte. Einige andere Funktionen von rvalue-Referenzen können jedoch nicht in C ++ 03 implementiert werden, sodass diesstd::forward
nicht möglich ist. Die andere Sache ist, dass dasstd::unique_ptr
nicht nützlich sein wird, da die Sammlungen die Verschiebungssemantik nur verwenden, wenn Sie sie alle ersetzen.unique_ptr
. Schauen Sie sich die Mängel anauto_ptr
.unique_ptr
ist praktisch das Lehrbuchbeispiel einer Klasse, deren Semantik durch das Sprachmerkmal grundlegend ermöglicht wurde.unique_ptr
dass die Sprachfunktion dies grundsätzlich ermöglicht hätte. Ohne diese Funktion wäre es jedoch nicht sehr nützlich. denn ohne perfekte Weiterleitung wäre es in vielen Fällen nicht verwendbar, und für eine perfekte Weiterleitung ist diese Funktion erforderlich.-std=c++0x
Option angeben , um sie zu aktivieren).std
Namespace ist ein "undefiniertes Verhalten". Das heißt, die Spezifikation sagt nicht, was passieren wird. Wenn Sie jedoch wissen, dass die Standardbibliothek auf einer bestimmten Plattform etwas nicht definiert, definieren Sie es einfach. Erwarten Sie nur, dass Sie auf jeder Plattform überprüfen müssen, was Sie benötigen und was Sie definieren können.unique_ptr
. Es wäre jedoch nicht allzu nützlich, da es sich auf Auflistungen stützt, die tatsächlich die Verschiebungssemantik verwenden, und die C ++ 03-Auflistungen offensichtlich nicht.quelle