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
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 comemy_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 neltr1
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 includonounique_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:
-
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.
-
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. -
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.
auto_ptr
scoped (ad esempiostd::auto_ptr
), è necessario o il puntatore intelligente può essere ottenuto da qualche altro spazio dei nomi?Foo::Initialize
inFoo::Foo
.