Wie gehe ich mit Designänderungen für die automatische Verachtung in C ++ 11 um?

Wir testen eine Bibliothek unter C ++ 11 (d. h. -std=c++11). Die Bibliothek verwendet auto_ptr und dieses Muster:

Foo* GetFoo() { autoptr<Foo> ptr(new Foo); // Initialize Foo ptr->Initialize(...); // Now configure remaining attributes ptr->SomeSetting(...); return ptr.release(); } 

C ++ 11 veraltet auto_ptr, also wollen wir uns davon entfernen.

Der Code unterstützt jedoch sowohl C ++ 03 als auch C ++ 11, so dass es nicht einfach ist, . Erwähnenswert ist auch, dass die Bibliothek keine externen Abhängigkeiten aufweist. Sie verwendet C ++ 03 und keine Autotools, Cmake, Boost, …

Wie sollten wir mit den Designänderungen umgehen, um sie zu entfernen? von auto_ptr für C ++ 11 unter Beibehaltung der Kompatibilität mit C ++ 03?

Kommentare

  • Sind einige der Bereiche auto_ptr (dh std::auto_ptr) erforderlich, oder muss der Smart Pointer aus einem anderen Namespace abgerufen werden?
  • Nebenbei möchten Sie möglicherweise Foo::Initialize in Foo::Foo falten.
  • @ MSalters – ja, das war schon immer eines der Dinge, bei denen ich mich leicht unwohl gefühlt habe. Die Bibliothek wurde in den 1990er Jahren entworfen, und ich denke, das Design war ähnlich wie bei MFC. Das heißt, es war niedriger Level C ++ Konstruktion und dann eine " übergeordnete " Objektkonstruktion. Ich denke, die Funktion wurde als Kompromiss verwendet, so dass Klassen nicht ' hat nicht 6 oder 12 verschiedene Konstruktoren. (Zu diesem Zeitpunkt wurde das, was ich getan habe, durchlaufen und sichergestellt, dass die Mitgliedsvariablen der POD-Typen in den C ++ – Konstruktoren auf vernünftige Standardeinstellungen initialisiert werden.)

Antwort

In den meisten Fällen wurde die std::unique_ptr als Drop-In erstellt (aber sicherer) Ersatz für std::auto_ptr , daher sollten nur sehr wenige (wenn überhaupt) Codeänderungen erforderlich sein, außer (wie Sie fragen) Weisen Sie den Code an, entweder unique_ptr oder auto_ptr zu verwenden.

Es gibt verschiedene Möglichkeiten Dies (und jedes hat seine eigenen Kompromisse) unten. Angesichts des bereitgestellten Codebeispiels würde ich eine der beiden ersten Optionen bevorzugen.

Option 1

#if __cplusplus >= 201103L template <typename T> using auto_ptr = std::unique_ptr<T>; #else using std::auto_ptr; #endif 

Kompromisse;

  • Sie führen den Namen auto_ptr in den globalen Namespace ein ;; Sie können dies abmildern, indem Sie definieren, dass es sich um Ihren eigenen " privaten " Namespace
  • handelt. Nach der Migration auf C ++ 17 (Ich glaube, auto_ptr wird vollständig entfernt) Sie können einfacher

Option 2

Kompromisse;

  • Die Arbeit ist wahrscheinlich umständlicher. Alle aktuellen auto_ptr müssen im Code auf geändert werden so etwas wie my_ptr<T>::ptr
  • Bessere Sicherheit Die Namen werden nicht in den globalen Namespace eingeführt.

Option 3

Etwas umstritten, aber wenn Sie bereit sind, die Vorbehalte zu ertragen, eine std -Klasse als Basis zu haben

#if __cplusplus >= 201103L template <typename T> using my_ptr = std::unique_ptr<T>; #else template <typename T> class my_ptr : public std::auto_ptr<T> { // implement the constructors for easier use // in particular explicit my_ptr( X* p = 0 ) : std::auto_ptr(p) {} }; #endif 

Kompromisse;

  • Versuchen Sie nicht, die geerbte Klasse zu verwenden, in der eine virtuelle Basis (insbesondere für den nicht virtuellen Destruktor) erwartet wird. Nicht, dass dies eine sein sollte Problem in dem Fall – aber seien Sie sich dessen bewusst
  • Auch hier ändert sich der Code
  • Mögliche Fehlanpassungen des Namespace – alles hängt davon ab, wie die Zeigerklasse verwendet wird, um mit

Option 4

zu beginnen

Wickeln Sie die Zeiger in eine neue Klasse und aggregieren Sie die erforderlichen Funktionen zum Mitglied

template <typename T> class my_ptr { // could even use auto_ptr name? #if __cplusplus >= 201103L std::unique_ptr<T> ptr_; #else std::auto_ptr<T> ptr_; #endif // implement functions required... T* release() { return ptr_.release(); } }; 

Kompromisse;

  • A. wenig extrem, wenn alles, was Sie wirklich wollen, darin besteht, " " die Implementierungen auszutauschen

Kommentare

  • Sehr gute Antwort. Ich habe es tatsächlich ein bisschen recherchiert, und Sie haben mindestens drei der Tests bestanden, die ich versucht habe. (Was Ihnen fehlt, sind die OS X- und Clang-spezifischen Dinge. OS X ist ein Bär, da es zeitweise immer noch den TR1-Namespace für C ++ 03 verwendet und Sie Dinge mit dieser Methode einschließen müssen: Kein Typ mit dem Namen ' unique_ptr ' im Namespace ' std ' beim Kompilieren unter LLVM / Clang ).
  • @jww. Ich ' bin unter OS X (XCode 6.4 und Apple LLVM Version 6.1.0 (clang-602.0.53) (basierend auf LLVM 3.6.0svn)) und habe keine Probleme mit dem C. ++ 03/11 Mix anders als der tr1 Namespace ist nicht mehr vorhanden (ich verwende libc ++ und nicht libstdc ++).Ich weiß, dass tr1 nicht normativ war, aber ich kann ' nirgendwo im Entwurf (hier) finden, dass die Dateien mussten überhaupt <tr1/...> sein, tatsächlich wird erwähnt, dass sie sich nur im Header befinden <memory> usw. Datei nur in der tr1 Namespace.
  • @jww. Ich denke, dass Sie bei einer bestimmten Mischung aus Compiler, Bibliothek und Zielgerät möglicherweise noch ein paar Handstände machen müssen. Andernfalls sollten Sie unter OS X auf clang und libc ++ umsteigen. Ehrlich gesagt betrachte ich libc ++ als die neue " native " C ++ – Bibliothek für OS X – ich würde dies standardmäßig tun. Ich habe keine Möglichkeit, diese Behauptungen zu unterstützen, außer dass die Geschichte der Clang / Apple-Beziehung und dass die GCC-Tools unter OS X veraltet (Bibliothek) oder einfach entfernt zu sein scheinen (soweit ich weiß, ist GCC ohnehin ein dünner Stich, um zu klirren ).
  • " Andernfalls sollten Sie unter OS X auf clang und libc ++ umsteigen … " – ja, ich stimme dir irgendwie zu. Wir möchten jedoch die Benutzer diese Wahl treffen lassen und sie ihnen nicht aufzwingen. (Sie treffen implizit die Wahl, wenn sie CXX=... angeben.)
  • Hier ist ' der Fall Das bereitet mir unter OS X 10.7 und 10.8 so viele Probleme: c++ -v -std=c++11 -x c++ - < /dev/null. Ich grep'd die Include-Verzeichnisse, die ausgegeben wurden, und sie enthalten nicht unique_ptr.

Antwort

Option 5: Direkter Alias.

#if __cplusplus >= 201103L template<typename T> using MyPtr = std::unique_ptr<T>; #else #define MyPtr std::auto_ptr #endif 

Kompromisse:

  1. Bei neueren Sprachversionen, AKA C ++ 11 und höher, wird Ihr Alias-Typ dem richtigen Smart Pointer zugeordnet. Jeder Benutzercode, der tatsächlich von APIs abhängt, die für std :: auto_ptr spezifisch sind, wird vom Compiler markiert. Dies ist die ultimative Garantie dafür, dass er wirklich behoben wird.

  2. In Legacy Im C ++ 03-Modus ist der Typalias ein Makro. Dies ist grob, aber die resultierende Syntax MyPtr<T> ist im Rest des Codes mit dem Fall C ++ 11 identisch.

  3. Sie müssen alle Ihre auto_ptr-Variablen finden und in MyPtr ändern, um dies einzurichten.

Kommentare

  • ' ist sehr unklar, worauf sich dies bezieht (und, wie formuliert, Es ist überhaupt keine ' Frage.
  • @autophage Ich glaube, es ist eine Antwort … also wahrscheinlich keine Frage.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.