Cum să gestionați modificările de proiectare pentru deprecierea auto_ptr în C ++ 11?

Testăm o bibliotecă sub C ++ 11 (adică, -std=c++11). Biblioteca utilizează auto_ptr și acest model:

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

C ++ 11 depreciat auto_ptr, așa că dorim să ne îndepărtăm de acesta.

Cu toate acestea, codul acceptă atât C ++ 03, cât și C ++ 11, deci nu este un simplu ca și divizarea auto_ptr. De asemenea, merită menționat că biblioteca nu are dependențe externe. Folosește C ++ 03; și nu folosește Autotools, Cmake, Boost, …

Cum ar trebui să gestionăm modificările de proiectare pentru a ne îndepărta de la auto_ptr pentru C ++ 11 păstrând în același timp compatibilitatea cu C ++ 03?

Comentarii

  • Există vreun domeniu auto_ptr (adică std::auto_ptr), trebuie să fie sau pointerul inteligent poate fi obținut din alt spațiu de nume?
  • Ca o parte, poate doriți să pliați Foo::Initialize în Foo::Foo.
  • @ MSalters – da, asta a fost întotdeauna unul dintre acele lucruri despre care m-am simțit ușor inconfortabil. Biblioteca a fost proiectată în anii 1990 și cred că designul a fost similar cu MFC. construcție de nivel C ++ și apoi o construcție de obiect " de nivel superior ". Cred că caracteristica a fost utilizată ca un compromis, deci clasele nu ' t au 6 sau 12 constructori diferiți. (În acest moment, ceea ce am făcut a trecut și am asigurat că variabilele membre ale tipurilor POD sunt inițializate la valorile implicite sănătoase în constructorii C ++).

Răspuns

În cele mai multe privințe, std::unique_ptr a fost introdus (dar mai sigur) înlocuire pentru std::auto_ptr , deci ar trebui să existe foarte puține (dacă există) modificări de cod necesare, altele decât (după cum întrebați) direcționarea codului pentru a utiliza fie unique_ptr, fie auto_ptr.

Există câteva modalități de a face acest lucru (și fiecare vine cu propriile compromisuri ale listei) de mai jos. Având în vedere exemplul de cod furnizat, aș favoriza oricare dintre primele două opțiuni .

Opțiunea 1

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

Compensări;

  • Introduceți numele auto_ptr în spațiul de nume global ; puteți atenua acest lucru definindu-l, este propriul dvs. spațiu de nume " privat "
  • După migrarea la C ++ 17 (Cred că auto_ptr va fi complet eliminat) puteți căuta și înlocui mai ușor

Opțiunea 2

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

Compensări;

  • Probabil mai greoaie pentru a lucra, toate auto_ptr curente trebuie schimbate în cod pentru ceva de genul my_ptr<T>::ptr
  • Siguranță mai bună, numele nu sunt introduse în spațiul de nume global

Opțiunea 3

Oarecum controversat, dar dacă sunteți pregătit să rezistați avertismentelor de a avea o clasă std ca bază

#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 

Compensări;

  • Nu încercați să utilizați clasa moștenită unde ar fi de așteptat o bază virtuală (în special wrt destructorul non-virtual). Nu că aceasta ar trebui să fie o problemă în caz – dar fiți conștienți de aceasta
  • Din nou, modificările de cod
  • Potențiale nepotriviri ale spațiului de nume – totul depinde de modul în care este utilizată clasa indicatorului pentru a începe cu

Opțiunea 4

Înfășurați indicii într-o nouă clasă și agregați funcțiile necesare la membru

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(); } }; 

Compensări;

  • A puțin extrem când tot ce îți dorești cu adevărat este să " să schimbi " implementările afară

Comentarii

  • Răspuns foarte bun. De fapt, l-am cercetat puțin și ai dat cel puțin trei dintre testele pe care le-am încercat. (Ceea ce îți lipsește este lucrurile specifice OS X și Clang. OS X este un urs, deoarece folosește uneori încă spațiu de nume TR1 pentru C ++ 03 și trebuie să incluzi lucruri folosind această metodă: Niciun tip numit ' unique_ptr ' în spațiul de nume ' std ' atunci când compilați sub LLVM / Clang ).
  • @jww. ' m pe OS X (XCode 6.4 și Apple LLVM versiunea 6.1.0 (clang-602.0.53) (bazat pe LLVM 3.6.0svn)) și nu am probleme cu C ++ 03/11 mix altele decât spațiul de nume tr1 nu mai există (folosesc libc ++ și nu libstdc ++).Știu că tr1 a fost non-normativ, dar nu pot ' să găsesc oriunde în schița (aici) că fișierele trebuiau să fie <tr1/...> deloc, de fapt menționează doar faptul că se află în antet <memory> etc. fișier doar în tr1 spațiu de nume.
  • @jww. Cred că, având în vedere un anumit amestec de compilator, bibliotecă și dispozitiv țintă – poate fi necesar să mai faceți câteva suporturi manuale. Altfel, pe OS X, luați în considerare trecerea la clang și libc ++. Sincer, consider că libc ++ este noua " nativă " bibliotecă C ++ în OS X – aș prefera acest lucru. Nu am nicio modalitate de a susține aceste afirmații, altele că istoria relației clang / Apple și că instrumentele GCC din OS X par învechite (bibliotecă) sau pur și simplu eliminate (din câte știu, GCC este oricum un butuc subțire ).
  • " Altfel, pe OS X, luați în considerare trecerea la clang și libc ++ … " – da, sunt cam de acord cu tine. Cu toate acestea, am dori să lăsăm utilizatorii să facă această alegere și să nu le impună. (Ei implicit fac alegerea atunci când specifică (sau lipsesc) CXX=...).
  • Aici ' este cazul asta îmi provoacă atât de multe probleme cu OS X 10.7 și 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Am grep'd directoarele de includere care au fost aruncate și nu includ unique_ptr.

Răspuns

Opțiunea 5: Alias direct.

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

Compensări:

  1. Pentru versiunile lingvistice mai noi, AKA C ++ 11 și versiunile ulterioare, tipul de alias se mapează cu indicatorul inteligent corect. Orice cod de utilizator care depinde de fapt de API-urile specifice std :: auto_ptr va fi marcat de compilator, ceea ce reprezintă garanția finală că „va fi cu adevărat remediat.

  2. În Legacy modul c ++ 03 aliasul de tip este o macro. Aceasta este brută, dar sintaxa rezultată MyPtr<T> va fi identică cu cazul C ++ 11 în restul codului.

  3. Trebuie să găsiți și să schimbați toate variabilele auto_ptr în MyPtr pentru a configura acest lucru.

Comentarii

  • ' este foarte neclar la ce se referă acest lucru (și, așa cum este formulat, nu este deloc o ' o întrebare).
  • @autophage Cred că este un răspuns … deci probabil că nu este o întrebare.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *