Hur hanterar jag designändringar för avskrivning av auto_ptr i C ++ 11?

Vi testar ett bibliotek under C ++ 11 (dvs. -std=c++11). Biblioteket använder auto_ptr och detta mönster:

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

C ++ 11 utfasad auto_ptr, så vi vill gå bort från det.

Koden stöder dock både C ++ 03 och C ++ 11, så det är inte så enkelt som att ge auto_ptr. Det är också värt att nämna att biblioteket inte har några externa beroenden. Det använder C ++ 03 och använder inte Autotools, Cmake, Boost, …

Hur ska vi hantera designförändringarna för att flytta bort från auto_ptr för C ++ 11 samtidigt som kompatibiliteten med C ++ 03 bibehålls?

Kommentarer

  • Finns någon av auto_ptr (dvs. std::auto_ptr), måste de vara eller kan den smarta pekaren fås från något annat namnområde?
  • Som en borta kan du lägga till Foo::Initialize till Foo::Foo.
  • @ MSalters – ja, det har alltid varit en av de saker jag känt mig lite obekväm med. Biblioteket designades på 1990-talet, och jag tycker att designen liknade MFC. Det vill säga det var lägre nivå C ++ konstruktion och sedan en " högre nivå " objektkonstruktion. Jag tror att funktionen användes som en avvägning så att klasserna inte ' t har 6 eller 12 olika konstruktörer. (Vid denna tidpunkt har det jag gjort gått igenom och säkerställt att medlemsvariablerna för POD-typerna initialiseras till förnuftiga standardvärden i C ++ -konstruktörerna.

Svar

I de flesta avseenden gjordes std::unique_ptr (men säkrare) ersättning för std::auto_ptr , så det borde vara väldigt få (om några) kodändringar som krävs förutom (som du frågar) riktar koden till att använda antingen unique_ptr eller auto_ptr.

Det finns några sätt att göra detta (och alla kommer med sina egna listavvägningar) nedan. Med tanke på det angivna kodprovet skulle jag föredra något av de två första alternativen .

Alternativ 1

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

Avvägningar;

  • Du introducerar auto_ptr namnet i det globala namnområdet ; du kan mildra detta genom att definiera att det är ditt eget " privat " namnområde
  • En gång migrera till C ++ 17 (Jag tror att auto_ptr kommer att tas bort helt) du kan lättare söka och ersätta

Alternativ 2

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

Avvägningar;

  • Förmodligen mer besvärliga att arbeta med, alla nuvarande auto_ptr måste ändras i koden till något som my_ptr<T>::ptr
  • Bättre säkerhet namnen införs inte i det globala namnområdet

Alternativ 3

Något kontroversiellt, men om du är beredd att stå ut med försiktigheten att ha en std -klass som bas

#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 

Avvägningar;

  • Försök inte använda den ärvda klassen där en virtuell bas (i synnerhet mot den icke-virtuella destruktorn) kan förväntas. Inte för att detta ska vara en fråga i fallet – men var medveten om det
  • Återigen ändras kod
  • Potentiella fel i namnrymden – allt beror på hur pekarklassen används till att börja med

Alternativ 4

Slå in pekarna i en ny klass och samla de nödvändiga funktionerna till medlemmen

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

Avvägningar;

  • A lite extremt när allt du verkligen vill är att " byta " implementeringarna ut

Kommentarer

  • Mycket bra svar. Jag undersökte det faktiskt lite, och du slog åtminstone tre av de test jag testade. (Det du saknar är OS X och Clang-specifika saker. OS X är en björn eftersom det fortfarande använder TR1-namnområde för C ++ 03 ibland, och du måste inkludera saker med den här metoden: Ingen typ med namnet ' unik_ptr ' i namnområdet ' std ' vid kompilering under LLVM / Clang ).
  • @jww. Jag ' m på OS X (XCode 6.4 och Apple LLVM version 6.1.0 (clang-602.0.53) (baserat på LLVM 3.6.0svn)) och har inga problem med C ++ 03/11-mix annat än tr1 namnområdet finns inte längre (jag använder libc ++ och inte libstdc ++).Jag vet att tr1 inte var normativ, men jag kan ' inte hitta någonstans i utkast (här) som filer måste vara <tr1/...> alls, faktiskt nämns det bara att vara i rubriken <memory> etc. filen bara i tr1 namnområde.
  • @jww. Jag antar att med tanke på en viss blandning av kompilator, bibliotek och målenhet – kan du behöva göra några fler handställ. Annars, på OS X, överväga att flytta till clang och libc ++. Uppriktigt sagt anser jag att libc ++ är det nya " infödda " C ++ – biblioteket till OS X – jag skulle som standard använda det. Jag har inget sätt att stödja dessa påståenden andra att historien om clang / Apple-förhållandet och att GCC-verktygen på OS X verkar vara föråldrade (bibliotek) eller bara borttagna (såvitt jag vet GCC är en tunn stub att klaga ändå
  • " I OS X kan du överväga att flytta till clang och libc ++ … " – ja, jag håller med dig. Vi vill dock låta användarna göra det valet och inte tvinga det på dem. (De gör implicit valet när de anger (eller saknar) CXX=...).
  • Här ' är fallet det orsakar mig så mycket besvär på OS X 10.7 och 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Jag grep'd inkluderar kataloger som dumpades och de inte inkluderar unique_ptr.

Svar

Alternativ 5: Direktalias.

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

Avvägningar:

  1. För nyare språkversioner, AKA C ++ 11 och senare, mappar din alias typ till rätt smart pekare. Alla användarkoder som faktiskt beror på API: er som är specifika för std :: auto_ptr kommer att flaggas av kompilatorn, vilket är den ultimata garantin för att den verkligen kommer att fixas.

  2. I Legacy c ++ 03-läge typ alias är ett makro. Detta är grovt, men den resulterande syntaxen MyPtr<T> kommer att vara identisk med C ++ 11-fallet under resten av koden.

  3. Du måste hitta och ändra alla dina auto_ptr-variabler till MyPtr för att ställa in detta.

Kommentarer

  • Det ' är mycket oklart vad detta hänvisar till (och, som formulerat, det är ingen ' alls en fråga).
  • @autophage Jag tror att det är ett svar … så förmodligen inte en fråga.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *