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


12

Stiamo testando una libreria in C ++ 11 (ovvero, -std=c++11). La libreria utilizza auto_ptre questo modello:

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

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

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

    return ptr.release();
}

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

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

Come dovremmo gestire le modifiche alla progettazione per allontanarci da auto_ptrC ++ 11 mantenendo la compatibilità con C ++ 03?


Esistono degli auto_ptrambiti (ovvero std::auto_ptr), devono essere o può essere ottenuto il puntatore intelligente da qualche altro spazio dei nomi?
Niall,

Per inciso, si consiglia di piegare Foo::Initializein Foo::Foo.
MSalters il

1
@MSalters - sì, è sempre stata una di quelle cose di cui mi sono sentito leggermente a disagio. La biblioteca è stata progettata negli anni '90 e penso che il design fosse simile all'MFC. Cioè, c'era una costruzione C ++ di livello inferiore e quindi una costruzione di oggetti "di livello superiore". Penso che la funzione sia stata usata come un compromesso, quindi le classi non hanno 6 o 12 costruttori diversi. (A questo punto, ciò che ho fatto è stato analizzato e assicurato che le variabili membro dei tipi POD siano inizializzate su valori predefiniti sani nei costruttori C ++).

Risposte:


13

Per la maggior parte degli aspetti, è std::unique_ptrstato creato un rimpiazzo (ma più sicuro) per la sostituzione std::auto_ptr, quindi dovrebbero esserci pochissime (se presenti) modifiche al codice diverse da (come richiesto) per indirizzare il codice a utilizzare unique_ptro auto_ptr.

Ci sono alcuni modi per farlo (e ognuno viene fornito con il proprio elenco di compromessi) di seguito. Dato l'esempio di codice 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 auto_ptrnome nello spazio dei nomi globale; puoi mitigarlo definendo che è il tuo spazio dei nomi "privato"
  • Una volta auto_ptreseguita la migrazione a C ++ 17 (credo che verrà completamente rimosso) è possibile 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ù ingombrante con cui lavorare, tutta l'attuale auto_ptrnecessità di cambiare il codice in qualcosa del generemy_ptr<T>::ptr
  • Migliore sicurezza, i nomi non vengono introdotti nello spazio dei nomi globale

Opzione 3

Un po 'controverso, ma se sei pronto a sopportare le avvertenze di avere una stdclasse 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 tentare di utilizzare la classe ereditata in cui ci si aspetterebbe una base virtuale (in particolare il distruttore non virtuale). Non che questo dovrebbe essere un problema nel caso, ma attenzione
  • Ancora una volta, il codice cambia
  • Potenziali disallineamenti dello spazio dei nomi: tutto dipende da come la classe puntatore viene utilizzata per iniziare

Opzione 4

Avvolgere i puntatori in una nuova classe e aggregare 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;

  • Un po 'estremo quando tutto ciò che vuoi veramente è "scambiare" le implementazioni

Ottima risposta In realtà l'ho studiato un po 'e hai superato almeno tre dei test che ho provato. (Quello che ti manca sono le cose specifiche di OS X e Clang. OS X è un orso perché a volte usa ancora lo spazio dei nomi TR1 per C ++ 03 e devi includere cose usando questo metodo: Nessun tipo chiamato '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 mix C ++ 03/11 diverso dallo tr1spazio dei nomi no essere più lì (uso libc ++ e non libstdc ++). So che tr1 era non normativo, ma non riesco a trovare da nessuna parte nella bozza (qui) che i file dovevano essere <tr1/...>affatto, infatti menziona solo il fatto di essere nel <memory>file dell'intestazione ecc. Solo nello tr1spazio dei nomi.
Niall,

@jww. Immagino che, dato un particolare mix di compilatore, libreria e dispositivo di destinazione, potrebbe essere necessario fare qualche altro supporto manuale. Altrimenti, su OS X, potresti passare a clang e libc ++. Francamente considero libc ++ come la nuova libreria "nativa" di C ++ su OS X - lo farei di default. Non ho modo di sostenere queste affermazioni a parte il fatto che la storia del rapporto clang / Apple e che gli strumenti GCC su OS X sembrano obsoleti (libreria) o appena rimossi (per quanto ne so GCC è un sottile stub che può comunque clangare ).
Niall,

"Altrimenti, su OS X, considera di passare a clang e libc ++ ..." - sì, sono abbastanza d'accordo con te. Tuttavia, vorremmo consentire agli utenti di fare questa scelta e non forzarla. (Implicitamente fanno la scelta quando specificano (o mancano) CXX=...).

Ecco il caso che mi sta causando così tanti problemi su OS X 10.7 e 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Ho grep'dl'includere le directory che sono state oggetto di dumping, e lo fanno non includo unique_ptr.

0

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 linguistiche più recenti, AKA C ++ 11 e versioni successive, il tipo di alias è associato al puntatore intelligente corretto. Qualsiasi codice utente che in realtà dipende dalle API specifiche di std :: auto_ptr verrà contrassegnato dal compilatore, che è la massima garanzia che verrà realmente corretto.

  2. In modalità c ++ 03 legacy il tipo alias è 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 MyPtrper impostarle.


1
Non è molto chiaro a cosa si riferisca (e, come espresso, non è affatto una domanda).
autofago

1
@autophage Credo sia una risposta ... quindi probabilmente non è una domanda.
Kain0_0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.