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

12

Wir testen eine Bibliothek unter C ++ 11 (dh -std=c++11). Die Bibliothek verwendet auto_ptrund dieses Muster:

Foo* GetFoo()
{
    autoptr<Foo> ptr(new Foo);

    // Initialize Foo
    ptr->Initialize(...);

    // Now configure remaining attributes
    ptr->SomeSetting(...);

    return ptr.release();
}

C ++ 11 ist veraltet auto_ptr, daher möchten wir uns davon entfernen.

Der Code unterstützt jedoch sowohl C ++ 03 als auch C ++ 11 auto_ptr. Erwähnenswert ist auch, dass die Bibliothek keine externen Abhängigkeiten aufweist. Es verwendet C ++ 03; und verwendet keine Autotools, Cmake, Boost, ...

Wie sollen wir mit den Designänderungen umgehen, um uns von auto_ptrC ++ 11 zu entfernen, während die Kompatibilität mit C ++ 03 erhalten bleibt?

MetaFight
quelle
Sind auto_ptreinige der Bereiche vorhanden (dh std::auto_ptrmüssen sie vorhanden sein oder kann der Smart Pointer aus einem anderen Namespace abgerufen werden?
Niall
Als beiseite, können Sie falten wollen Foo::Initializein Foo::Foo.
MSalters
1
@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 gab eine C ++ - Konstruktion auf niedrigerer Ebene und dann eine Objektkonstruktion auf "höherer Ebene". Ich denke, das Feature wurde als Kompromiss verwendet, sodass Klassen keine 6 oder 12 verschiedenen Konstruktoren haben. (Zu diesem Zeitpunkt wurde das, was ich getan habe, durchlaufen und sichergestellt, dass die Elementvariablen der POD-Typen in den C ++ - Konstruktoren mit vernünftigen Standardwerten initialisiert werden.)

Antworten:

13

In den meisten Hinsichten wurde das std::unique_ptrals Ersatz (aber sicherer) gemacht std::auto_ptr, so dass nur sehr wenige (wenn überhaupt) Codeänderungen erforderlich sein sollten, außer (wie Sie verlangen), den Code entweder unique_ptroder zu verwenden auto_ptr.

Es gibt einige Möglichkeiten, dies zu tun (und jede hat ihre eigenen Listenkompromisse). 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ügen den auto_ptrNamen in den globalen Namespace ein. Sie können dies abschwächen, indem Sie festlegen, dass es sich um Ihren eigenen "privaten" Namespace handelt
  • Sobald Sie auf C ++ 17 migriert haben (ich glaube, das auto_ptrwird komplett entfernt), können Sie einfacher suchen und ersetzen

Option 2

template <typename T>
struct my_ptr {
    #if __cplusplus >= 201103L
    typedef std::unique_ptr<T> ptr;
    #else
    typedef std::auto_ptr<T> ptr;
    #endif
};

Kompromisse;

  • Wahrscheinlich umständlicher in der Arbeit, der gesamte aktuelle auto_ptrCode muss auf so etwas wie geändert werdenmy_ptr<T>::ptr
  • Aus Sicherheitsgründen werden die Namen nicht in den globalen Namespace übernommen

Option 3

Etwas umstritten, aber wenn Sie bereit sind, die Einschränkungen einer stdKlasse als Basis in Kauf zu nehmen

#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, bei der eine virtuelle Basis (insbesondere für den nicht virtuellen Destruktor) erwartet wird. Nicht, dass dies ein Problem sein sollte - aber seien Sie sich dessen bewusst
  • Auch hier ändert sich der Code
  • Mögliche Namespace-Konflikte - alles hängt davon ab, wie die Zeigerklasse verwendet wird

Option 4

Schließen Sie die Zeiger in eine neue Klasse ein und aggregieren Sie die erforderlichen Funktionen für das 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;

  • Ein wenig extrem, wenn Sie nur die Implementierungen "austauschen" möchten
Niall
quelle
Sehr gute Antwort. Ich habe ein bisschen recherchiert und du hast mindestens drei der Tests, die ich ausprobiert habe, bestanden. (Was Ihnen fehlt, ist das OS X- und Clang-spezifische Zeug. OS X ist ein Bär, weil es zeitweise immer noch den TR1-Namespace für C ++ 03 verwendet, und Sie müssen Dinge mit dieser Methode einfügen : Kein Typ namens 'unique_ptr' im Namespace 'std' beim Kompilieren unter LLVM / Clang ).
@jww. Ich arbeite 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 außer dem tr1Namespace Nr länger da sein (ich benutze libc ++ und nicht libstdc ++). Ich weiß, dass tr1 nicht normativ war, aber ich kann nirgendwo im Entwurf (hier) feststellen, dass die Dateien <tr1/...>überhaupt vorhanden sein mussten. Infact erwähnt, dass sie sich nur im Header <memory>usw. befinden, nur im tr1Namespace.
Niall
@jww. Ich vermute, dass Sie bei einer bestimmten Mischung aus Compiler, Bibliothek und Zielgerät möglicherweise noch ein paar Handstände mehr machen müssen. Andernfalls ziehen Sie unter OS X die Umstellung auf clang und libc ++ in Betracht. Ehrlich gesagt betrachte ich die libc ++ als die neue "native" C ++ - Bibliothek für OS X - ich würde dies standardmäßig tun. Ich kann diese Behauptungen nicht unterstützen, außer dass die Geschichte der Beziehung zwischen Clang und Apple und die GCC-Tools unter OS X veraltet (Bibliothek) oder einfach entfernt zu sein scheinen (soweit ich weiß, ist GCC sowieso ein dünner Stummel, der zu klingen ist ).
Niall
"Andernfalls überlegen Sie unter OS X, ob Sie zu clang und libc ++ wechseln ..." - ja, ich stimme Ihnen irgendwie zu. Wir möchten jedoch, dass die Benutzer diese Auswahl treffen und sie nicht dazu zwingen. (Sie treffen implizit die Wahl, wenn sie spezifizieren (oder fehlen) CXX=...).
Hier ist der Fall , dass mir so viel Mühe auf OS X 10.7 und 10.8 verursacht: c++ -v -std=c++11 -x c++ - < /dev/null. Ich grep'ddie Include - Verzeichnisse , die abgeladen wurden, und sie nicht enthalten unique_ptr.
0

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 repariert wird.

  2. Im Legacy-C ++ 03-Modus ist der Typ-Alias ​​ein Makro. Dies ist grob, aber die resultierende Syntax MyPtr<T>ist im Rest des Codes identisch mit der von C ++ 11.

  3. Sie müssen alle auto_ptr-Variablen MyPtrin suchen und ändern , um dies einzurichten.

user3726672
quelle
1
Es ist sehr unklar, worauf sich das bezieht (und wie gesagt, es ist überhaupt keine Frage).
autophage
1
@autophage Ich glaube, es ist eine Antwort ... also wahrscheinlich keine Frage.
Kain0_0