Jak poradzić sobie ze zmianami w projekcie dla wycofania auto_ptr w C ++ 11?

Testujemy bibliotekę w C ++ 11 (tj. -std=c++11). Biblioteka używa auto_ptr i tego wzorca:

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

C ++ 11 przestarzały auto_ptr, więc chcemy od tego odejść.

Jednak kod obsługuje zarówno C ++ 03, jak i C ++ 11, więc nie jest to takie proste, jak szarpanie auto_ptr. Warto również wspomnieć, że biblioteka nie ma żadnych zewnętrznych zależności. Używa C ++ 03; i nie używa Autotools, Cmake, Boost, …

Jak powinniśmy radzić sobie ze zmianami projektu, aby odejść from auto_ptr dla C ++ 11 przy zachowaniu zgodności z C ++ 03?

Komentarze

  • Czy któryś z auto_ptr ma określony zakres (tj. std::auto_ptr), czy muszą być lub czy inteligentny wskaźnik można uzyskać z innej przestrzeni nazw?
  • Na marginesie możesz złożyć Foo::Initialize w Foo::Foo.
  • @ MSalters – tak, to zawsze była jedna z tych rzeczy, z którymi czułem się lekko nieswojo. Biblioteka została zaprojektowana w latach 90. i myślę , że projekt był podobny do MFC. To znaczy, że była niższa konstrukcja poziomu C ++, a następnie " wyższy poziom " konstrukcja obiektu. Myślę, że ta funkcja została wykorzystana jako kompromis, więc klasy nie ' t mieć 6 lub 12 różnych konstruktorów. (W tym momencie przeszedłem przez to, co zrobiłem i upewniłem się, że zmienne składowe typów POD są zainicjowane jako rozsądne wartości domyślne w konstruktorach C ++).

Odpowiedź

Pod wieloma względami std::unique_ptr został stworzony po to, by go upuścić (ale bezpieczniejsze) zamiennik dla std::auto_ptr , więc powinno być bardzo mało (jeśli w ogóle) wymaganych zmian kodu poza (jak pytasz) kierując kod tak, aby używał unique_ptr lub auto_ptr.

Jest kilka sposobów na zrobienie tego to (i każdy ma własną listę kompromisów) poniżej. Biorąc pod uwagę dostarczony przykład kodu, wolałbym jedną z dwóch pierwszych opcji .

Opcja 1

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

Kompromisy;

  • Wprowadzasz nazwę auto_ptr do globalnej przestrzeni nazw ; możesz to złagodzić, definiując, że jest to Twoja własna " prywatna " przestrzeń nazw
  • Po migracji do C ++ 17 (Myślę, że auto_ptr zostanie całkowicie usunięte) możesz łatwiej wyszukiwać i zamieniać

Opcja 2

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

Kompromisy;

  • Prawdopodobnie bardziej kłopotliwe w obsłudze, wszystkie obecne auto_ptr wymagają zmiany w kodzie na coś w rodzaju my_ptr<T>::ptr
  • Lepsze bezpieczeństwo nazwy nie są wprowadzane do globalnej przestrzeni nazw

Opcja 3

Dość kontrowersyjne, ale jeśli jesteś gotów pogodzić się z zastrzeżeniami związanymi z posiadaniem klasy std jako podstawy

#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 

Kompromisy;

  • Nie próbuj używać odziedziczonej klasy, w której oczekiwana byłaby wirtualna baza (w szczególności z niewirtualnym destruktorem). Nie znaczy to, że powinno to być problem w przypadku – ale pamiętaj o tym
  • Ponownie, zmiany w kodzie
  • Potencjalne niezgodności przestrzeni nazw – wszystko zależy od tego, w jaki sposób klasa wskaźnika jest używana na początku

Opcja 4

Zawiń wskaźniki w nowej klasie i zagreguj wymagane funkcje do elementu członkowskiego

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

Kompromisy;

  • A trochę ekstremalne, gdy wszystko, czego naprawdę chcesz, to " podmiana " implementacje wyłączone

Komentarze

  • Bardzo dobra odpowiedź. Trochę to zbadałem i trafiłeś przynajmniej w trzy testy, które wypróbowałem. (Brakuje ci elementów specyficznych dla OS X i Clang. OS X jest niedźwiedziem, ponieważ czasami używa przestrzeni nazw TR1 dla C ++ 03 i musisz uwzględnić rzeczy za pomocą tej metody: Brak typu o nazwie ' unique_ptr ' w przestrzeni nazw ' std ' podczas kompilacji pod LLVM / Clang ).
  • @jww. Mam ' m na OS X (XCode 6.4 i Apple LLVM w wersji 6.1.0 (clang-602.0.53) (w oparciu o LLVM 3.6.0svn)) i nie mam problemów z C Mikser ++ 03/11 inny niż przestrzeń nazw tr1 już nie istnieje (używam libc ++ a nie libstdc ++).Wiem, że tr1 nie był normatywny, ale nie mogę ' znaleźć w wersji roboczej (tutaj) , że pliki musiały być w ogóle <tr1/...>, w rzeczywistości wspomina o byciu w nagłówku <memory> itd. plik tylko w tr1 przestrzeń nazw.
  • @jww. Wydaje mi się, że biorąc pod uwagę konkretną mieszankę kompilatora, biblioteki i urządzenia docelowego – być może będziesz musiał zrobić kilka więcej stojaków na ręce. W przeciwnym razie w systemie OS X rozważ przejście na clang i libc ++. Szczerze mówiąc, uważam libc ++ za nową " natywną bibliotekę " C ++ dla OS X – domyślnie bym to zrobił. Nie mam możliwości poparcia tych twierdzeń poza tym, że historia relacji clang / Apple i że narzędzia GCC na OS X wydają się nieaktualne (biblioteka) lub po prostu usunięte (o ile wiem, GCC jest cienkim kawałkiem do zadzwonienia i tak) ).
  • " W przeciwnym razie w systemie OS X rozważ przejście do clang i libc ++ … " – tak, trochę się z tobą zgadzam. Chcielibyśmy jednak pozwolić użytkownikom na dokonanie takiego wyboru, a nie narzucać im tego. (Niejawnie dokonują wyboru, gdy określają (lub ich brak) CXX=...).
  • Tutaj ' tak to sprawia mi tyle problemów w OS X 10.7 i 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. grep'd uwzględniam katalogi, które zostały zrzucone, i not obejmują unique_ptr.

Odpowiedź

Opcja 5: bezpośredni alias.

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

Kompromisy:

  1. W nowszych wersjach językowych, AKA C ++ 11 i nowszych, typ aliasu jest mapowany na właściwy inteligentny wskaźnik. Każdy kod użytkownika, który faktycznie zależy od API specyficznych dla std :: auto_ptr, zostanie oznaczony przez kompilator, co jest ostateczną gwarancją, że zostanie naprawiony.

  2. W starszej wersji tryb c ++ 03 alias typu jest makrem. To jest brutto, ale wynikowa składnia MyPtr<T> będzie identyczna jak w przypadku C ++ 11 w pozostałej części kodu.

  3. Aby to ustawić, musisz znaleźć i zmienić wszystkie zmienne auto_ptr na MyPtr.

Komentarze

  • To ' jest bardzo niejasne, do czego się odnosi (i zgodnie z frazą to nie jest ' to pytanie).
  • @autophage Uważam, że to odpowiedź … więc prawdopodobnie nie jest to pytanie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *