Come gestire le modifiche al design per la deprecazione di auto_ptr in C ++ 11?

Stiamo testando una libreria in C ++ 11 (ad esempio, -std=c++11). La libreria utilizza auto_ptr e questo pattern:

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

C ++ 11 deprecato auto_ptr, quindi vogliamo allontanarci da esso.

Tuttavia, il codice supporta sia C ++ 03 che C ++ 11, quindi non è semplice come strappare auto_ptr. Vale anche la pena ricordare che la libreria non ha dipendenze esterne. Utilizza C ++ 03; e non utilizza Autotools, Cmake, Boost, …

Come dovremmo gestire le modifiche al design per allontanarci da auto_ptr per C ++ 11 pur mantenendo la compatibilità con C ++ 03?

Commenti

  • Qualcuno dei auto_ptr scoped (ad esempio std::auto_ptr), è necessario o il puntatore intelligente può essere ottenuto da qualche altro spazio dei nomi?
  • Per inciso, potresti piegare Foo::Initialize in Foo::Foo.
  • @ MSalters: sì, questa è sempre stata una di quelle cose per cui mi sono sentito leggermente a disagio. La libreria è stata progettata negli anni 90 e penso che il design fosse simile a MFC. Cioè, era inferiore costruzione di oggetti di livello C ++, quindi una costruzione di oggetti " di livello superiore ". Penso che la funzione sia stata utilizzata come compromesso, quindi le classi non ' t avere 6 o 12 costruttori diversi. (A questo punto, quello che ho fatto è stato completato e mi sono assicurato che le variabili membro dei tipi POD siano inizializzate su valori predefiniti sani nei costruttori C ++).

Risposta

Per molti aspetti il std::unique_ptr è stato creato per essere incluso (ma più sicura) sostituzione per std::auto_ptr , quindi dovrebbero essere necessarie pochissime (se presenti) modifiche al codice oltre a (come chiedi) indicando al codice di utilizzare unique_ptr o auto_ptr.

Ci sono alcuni modi per farlo questo (e ciascuno viene fornito con il proprio elenco di compromessi) di seguito. Dato il codice di esempio fornito, preferirei una delle prime due opzioni .

Opzione 1

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

Compromessi;

  • Introduci il nome auto_ptr nello spazio dei nomi globale ; puoi mitigare questo problema definendo che sia il tuo " privato " spazio dei nomi
  • Una volta migrato a C ++ 17 (Credo che auto_ptr sarà completamente rimosso) puoi cercare e sostituire più facilmente

Opzione 2

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

Compromessi;

  • Probabilmente più complicato con cui lavorare, tutti gli auto_ptr correnti devono essere modificati nel codice per qualcosa come my_ptr<T>::ptr
  • Maggiore sicurezza, i nomi non vengono introdotti nello spazio dei nomi globale

Opzione 3

Un po controverso, ma se sei pronto a sopportare gli avvertimenti di avere una std classe come base

#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 

Compromessi;

  • Non provare a usare la classe ereditata in cui ci si aspetterebbe una base virtuale (in particolare rispetto al distruttore non virtuale). Non che dovrebbe essere un problema nel caso, ma sii consapevole di esso
  • Di nuovo, il codice cambia
  • Potenziali mancate corrispondenze dello spazio dei nomi: tutto dipende da come viene utilizzata la classe del puntatore per iniziare

Opzione 4

Racchiudi i puntatori in una nuova classe e aggrega le funzioni richieste al membro

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

Compromessi;

  • A un po estremo quando tutto ciò che vuoi veramente è " scambiare " le implementazioni

Commenti

  • Ottima risposta. In realtà ho studiato un po e hai superato almeno tre dei test che ho provato. (Quello che ti manca è la roba specifica per OS X e Clang. OS X è un orso perché usa ancora lo spazio dei nomi TR1 per C ++ 03 a volte, e devi includere cose usando questo metodo: Nessun tipo denominato ' unique_ptr ' nello spazio dei nomi ' std ' durante la compilazione in LLVM / Clang ).
  • @jww. Sono ' su OS X (XCode 6.4 e Apple LLVM versione 6.1.0 (clang-602.0.53) (basato su LLVM 3.6.0svn)) e non ho problemi con il C ++ 03/11 mix diverso dallo spazio dei nomi tr1 che non è più presente (io uso libc ++ e non libstdc ++).So che tr1 non era normativa, ma non posso ' trovare da nessuna parte nella bozza (qui) che la i file dovevano essere <tr1/...>, infatti menziona solo lessere nellintestazione <memory> ecc. file solo nel tr1 spazio dei nomi.
  • @jww. Immagino che, data una particolare combinazione di compilatore, libreria e dispositivo di destinazione, potrebbe essere necessario fare qualche altro supporto per le mani. Altrimenti, su OS X, considera il passaggio a clang e libc ++. Francamente considero la libc ++ come la nuova libreria C ++ " native " per OS X – io per impostazione predefinita lo userei. Non ho modo di supportare queste affermazioni, a parte il fatto che la storia della relazione clang / Apple e che gli strumenti GCC su OS X sembrano obsoleti (libreria) o semplicemente rimossi (per quanto ne so GCC è un sottile stub da clangare comunque ).
  • " Altrimenti, su OS X, valuta la possibilità di passare a clang e libc ++ … " – sì, sono daccordo con te. Tuttavia, vorremmo consentire agli utenti di fare questa scelta e non costringerla a farlo. (Fanno la scelta implicitamente quando specificano (o mancano) CXX=...).
  • Ecco ' il caso questo mi sta causando così tanti problemi su OS X 10.7 e 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. grep'd le directory di inclusione di cui è stato eseguito il dumping e non includono unique_ptr.

Risposta

Opzione 5: alias diretto.

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

Compromessi:

  1. Per le versioni in lingue più recenti, AKA C ++ 11 e successive, il tipo di alias viene mappato al puntatore intelligente corretto. Qualsiasi codice utente che in realtà dipende dalle API specifiche di std :: auto_ptr verrà contrassegnato dal compilatore, il che è la massima garanzia che verrà davvero corretto.

  2. In Legacy modalità c ++ 03 lalias di tipo è una macro. Questo è grossolano, ma la sintassi risultante MyPtr<T> sarà identica al caso C ++ 11 per tutto il resto del codice.

  3. Devi trovare e modificare tutte le tue variabili auto_ptr in MyPtr per impostarlo.

Commenti

  • ' è molto poco chiaro a cosa si riferisca (e, come detto, non è ' una domanda).
  • @autophage credo che sia una risposta … quindi probabilmente non una domanda.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *