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
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 sommy_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 itr1
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
. Jaggrep'd
inkluderar kataloger som dumpades och de inte inkluderarunique_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:
-
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.
-
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. -
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.
auto_ptr
(dvs.std::auto_ptr
), måste de vara eller kan den smarta pekaren fås från något annat namnområde?Foo::Initialize
tillFoo::Foo
.