Как обрабатывать изменения дизайна для устаревания auto_ptr в C ++ 11?

12

Мы тестируем библиотеку под C ++ 11 (т.е. -std=c++11). Библиотека использует auto_ptrи этот шаблон:

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

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

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

    return ptr.release();
}

C ++ 11 устарел auto_ptr, поэтому мы хотим от него отойти.

Тем не менее, код поддерживает как C ++ 03, так и C ++ 11, поэтому он не такой простой, как янки auto_ptr. Стоит также упомянуть, что библиотека не имеет внешних зависимостей. Он использует C ++ 03; и не использует Autotools, Cmake, Boost, ...

Как мы должны обрабатывать изменения дизайна, чтобы отойти от auto_ptrC ++ 11 при сохранении совместимости с C ++ 03?

MetaFight
источник
Являются ли какие-либо из auto_ptrобластей (то есть std::auto_ptr), они должны быть или умный указатель может быть получен из некоторого другого пространства имен?
Найл
Кроме того, вы можете захотеть сложить Foo::Initializeв Foo::Foo.
MSalters
1
@MSalters - да, это всегда было одной из тех вещей, в которых я чувствовал себя немного неловко. Библиотека была разработана в 1990-х годах, и я думаю, что дизайн был похож на MFC. То есть была конструкция С ++ более низкого уровня, а затем конструкция объекта «более высокого уровня». Я думаю, что эта функция была использована в качестве компромисса, поэтому классы не имеют 6 или 12 различных конструкторов. (На этом этапе все, что я сделал, прошло, и мы убедились, что переменные-члены типов POD инициализируются в разумные значения по умолчанию в конструкторах C ++).

Ответы:

13

В большинстве отношениях std::unique_ptrбыло сделано , чтобы быть падение (но безопаснее) замены для std::auto_ptr, так что должно быть очень мало (если таковые имеются) изменения кода требуется , кроме (как вы спрашиваете) направлять код для использования либо unique_ptrили auto_ptr.

Есть несколько способов сделать это (и каждый из них имеет свой собственный компромисс со списком) ниже. Учитывая предоставленный пример кода, я бы предпочел любой из первых двух вариантов .

Опция 1

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

Компромисс;

  • Вы вводите auto_ptrимя в глобальное пространство имен; Вы можете смягчить это, определив, что это ваше собственное "личное" пространство имен
  • После перехода на C ++ 17 (я думаю, что auto_ptrон будет полностью удален), вам будет проще искать и заменять

Вариант 2

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

Компромисс;

  • Наверное, с этим труднее работать, все текущие изменения auto_ptrдолжны быть изменены в коде на что-то вродеmy_ptr<T>::ptr
  • Для большей безопасности имена не вводятся в глобальное пространство имен

Вариант 3

Несколько спорны, но если вы готовы мириться с предостережениями , имеющим stdкласс в качестве основы

#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

Компромисс;

  • Не пытайтесь использовать унаследованный класс, где ожидается виртуальная база (в частности, не виртуальный деструктор). Не то чтобы это должно было быть проблемой в деле - но имейте это в виду
  • Опять же, изменения кода
  • Потенциальное несоответствие пространства имен - все зависит от того, как класс указателя используется для начала

Вариант 4

Оберните указатели в новый класс и объедините необходимые функции для члена

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

Компромисс;

  • Немного экстрима, когда все, что вам действительно нужно, это «поменять» реализации
Найл
источник
Очень хороший ответ Я на самом деле немного его исследовал, и вы прошли как минимум три теста, которые я пробовал. (Чего вам не хватает, так это специфических вещей для OS X и Clang. OS X является медвежьим, потому что он все еще время от времени использует пространство имен TR1 для C ++ 03, и вы должны включать вещи, используя этот метод: нет типа с именем «unique_ptr» в пространстве имен 'std' при компиляции под LLVM / Clang ).
@jww. Я нахожусь на OS X (XCode 6.4 и Apple LLVM версии 6.1.0 (clang-602.0.53) (на основе LLVM 3.6.0svn)) и у меня нет проблем с миксом C ++ 03/11, кроме tr1пространства имен no там больше (я использую libc ++, а не libstdc ++). Я знаю, что tr1 был ненормативным, но я нигде не могу найти в черновике (здесь), что файлы должны быть <tr1/...>вообще, в действительности он упоминает только о том, что находится в <memory>файле заголовка и т. Д. Только в tr1пространстве имен.
Найл
@jww. Я предполагаю, что с учетом конкретного сочетания компилятора, библиотеки и целевого устройства - вам может понадобиться сделать еще несколько ручных стоек. Иначе, в OS X, подумайте о переходе на clang и libc ++. Честно говоря, я считаю libc ++ новой «родной» библиотекой C ++ для OS X - я бы по умолчанию это сделал. У меня нет никакого способа поддержать эти утверждения, за исключением того, что история отношений clang / Apple и что инструменты GCC в OS X кажутся устаревшими (библиотека) или просто удалены (насколько я знаю, GCC - тонкий тупик, чтобы в любом случае звонить) ).
Найл
«Иначе, на OS X, подумайте о переходе на clang и libc ++ ...» - да, я вроде с вами согласен. Однако мы бы хотели, чтобы пользователи сделали этот выбор, а не навязывали его им. (Они неявно делают выбор, когда указывают (или отсутствуют) CXX=...).
Вот случай , который вызывает у меня столько проблем на OS X 10.7 и 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Я grep'dвключил каталоги, которые были сброшены, и они не включают unique_ptr.
0

Вариант 5: Прямой псевдоним.

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

Компромисс:

  1. Для более новых языковых версий, AKA C ++ 11 и более поздних версий, ваш тип псевдонима соответствует правильному интеллектуальному указателю. Любой пользовательский код, который фактически зависит от API, специфичных для std :: auto_ptr, будет помечен компилятором, что является окончательной гарантией того, что он действительно будет исправлен.

  2. В режиме Legacy c ++ 03 псевдоним типа является макросом. Это грубо, но результирующий синтаксис MyPtr<T>будет идентичен случаю C ++ 11 в остальной части кода.

  3. Вы должны найти и изменить все ваши переменные auto_ptr MyPtr, чтобы настроить это.

user3726672
источник
1
Очень неясно, на что это ссылается (и, как сформулировано, это вообще не вопрос).
autophage
1
@autophage Я верю, что это ответ ... так что, вероятно, не вопрос.
Kain0_0