Jak obsłużyć zmiany w projekcie dla wycofania auto_ptr w C ++ 11?

12

Testujemy bibliotekę pod C ++ 11 (tj -std=c++11.). Biblioteka korzysta z auto_ptrtego wzoru:

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

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

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

    return ptr.release();
}

C ++ 11 jest przestarzałe auto_ptr, więc chcemy się od niego 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 zewnętrznych zależności. Używa C ++ 03; i nie używa Autotools, Cmake, Boost, ...

Jak powinniśmy poradzić sobie ze zmianami w projekcie, aby odejść auto_ptrdla C ++ 11, zachowując zgodność z C ++ 03?

MetaFight
źródło
Czy któryś z auto_ptrzakresów (tj. std::auto_ptr), Czy musi być, czy też inteligentny wskaźnik można uzyskać z innej przestrzeni nazw?
Niall
Tak na marginesie, można złożyć Foo::Initializedo Foo::Foo.
MSalters
1
@MSalters - tak, to zawsze była jedna z tych rzeczy, w których czułam się lekko nieswojo. Biblioteka została zaprojektowana w latach 90. i myślę, że projekt był podobny do MFC. Oznacza to, że istniała konstrukcja C ++ niższego poziomu, a następnie konstrukcja obiektu „wyższego poziomu”. Myślę, że ta funkcja została wykorzystana jako kompromis, więc klasy nie mają 6 lub 12 różnych konstruktorów. (W tym momencie to, co zrobiłem, zostało sprawdzone i upewniłem się, że zmienne składowe typów POD są inicjowane do rozsądnych wartości domyślnych w konstruktorach C ++).

Odpowiedzi:

13

W większości względami std::unique_ptrpowstał być spadek (ale bezpieczniej) zamiennik std::auto_ptr, więc nie powinno być bardzo niewiele (jeśli w ogóle) wymaga zmiany kodu innego niż (jak pytasz) kierowanie kod do używania albo unique_ptralbo auto_ptr.

Istnieje kilka sposobów, aby to zrobić (i każdy ma swoje własne kompromisy listy) poniżej. Biorąc pod uwagę przykładowy kod, 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 auto_ptrnazwę do globalnej przestrzeni nazw; możesz to złagodzić, definiując swoją własną „prywatną” przestrzeń nazw
  • Po migracji do C ++ 17 (uważam, że auto_ptrzostanie całkowicie usunięty) 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łopotliwa w pracy, cała obecna auto_ptrpotrzeba zmiany w kodzie na coś podobnegomy_ptr<T>::ptr
  • Dla większego bezpieczeństwa nazwy nie są wprowadzane do globalnej przestrzeni nazw

Opcja 3

Nieco kontrowersyjne, ale jeśli jesteś gotowy pogodzić się z zastrzeżeniami dotyczącymi posiadania stdklasy 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 spodziewana byłaby wirtualna baza (w szczególności nie-wirtualny destruktor). Nie to powinno być problemem w tej sprawie - ale pamiętaj o tym
  • Ponownie zmiany kodu
  • Potencjalne niedopasowania przestrzeni nazw - wszystko zależy od tego, w jaki sposób używana jest klasa wskaźnika

Opcja 4

Zawiń wskaźniki w nową klasę i agreguj 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;

  • Trochę ekstremalne, gdy wszystko, czego naprawdę chcesz, to „zamienić” implementacje
Niall
źródło
Bardzo dobra odpowiedź. Właściwie to trochę przestudiowałem, a ty zaliczyłeś co najmniej trzy testy, które próbowałem. (Brakuje tylko elementów specyficznych dla OS X i Clanga. OS X jest niedźwiedziem, ponieważ czasami używa przestrzeni nazw TR1 dla C ++ 03, i musisz uwzględnić rzeczy przy użyciu tej metody: Nie ma typu o nazwie „unikatowy_ptr” w przestrzeni nazw „std” przy kompilacji pod LLVM / Clang ).
@jww. Korzystam z systemu OS X (XCode 6.4 i Apple LLVM wersja 6.1.0 (clang-602.0.53) (w oparciu o LLVM 3.6.0svn)) i nie mam problemów z mieszaniem C ++ 03/11 innym niż tr1przestrzeń nazw nie już tam jestem (używam libc ++, a nie libstdc ++). Wiem, że tr1 był nienormatywny, ale nie mogę nigdzie znaleźć w szkicu (tutaj), że pliki <tr1/...>w ogóle musiały być , w rzeczywistości wspomina tylko o byciu w <memory>pliku nagłówka itp. Tylko w tr1przestrzeni nazw.
Niall
@jww. Wydaje mi się, że biorąc pod uwagę konkretną kombinację kompilatora, biblioteki i urządzenia docelowego - może być konieczne wykonanie kilku dodatkowych operacji ręcznych. W przeciwnym razie w 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 mogę w żaden sposób poprzeć tych twierdzeń, poza tym, że historia relacji clang / Apple i że narzędzia GCC w systemie OS X wydają się nieaktualne (biblioteka) lub po prostu zostały usunięte (o ile wiem, GCC to i tak cienki odcinek do kliknięcia) ).
Niall
„W innym przypadku, w OS X, rozważ przejście na clang i libc ++ ...” - tak, zgadzam się z tobą. Chcielibyśmy jednak pozwolić użytkownikom na dokonanie takiego wyboru, a nie narzucanie go. (Pośrednio dokonują wyboru, gdy określają (lub brakuje) CXX=...).
Oto przypadek, który jest przyczyną mi tyle kłopotów na OS X 10.7 i 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. I grep'dobejmują katalogi, które zostały zrzucone i nie zawierają unique_ptr.
0

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 przypadku nowszych wersji językowych, AKA C ++ 11 i nowszych, alias jest odwzorowywany na poprawny inteligentny wskaźnik. Każdy kod użytkownika, który faktycznie zależy od API specyficznych dla std :: auto_ptr, zostanie oflagowany przez kompilator, co jest ostateczną gwarancją, że naprawdę zostanie naprawiony.

  2. W trybie Legacy c ++ 03 alias typu to makro. Jest to obrzydliwe, ale wynikowa składnia MyPtr<T>będzie identyczna jak w przypadku C ++ 11 w pozostałej części kodu.

  3. Musisz znaleźć i zmienić wszystkie zmienne auto_ptr na MyPtr, aby to skonfigurować.

użytkownik3726672
źródło
1
Jest bardzo niejasne, do czego to się odnosi (i, jak to sformułowano, wcale nie jest pytaniem).
autofag
1
@autophage Wierzę, że to odpowiedź ... więc prawdopodobnie nie pytanie.
Kain0_0