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
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 rodzajumy_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 wtr1
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:
-
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.
-
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. -
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.
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?Foo::Initialize
wFoo::Foo
.