¿Cómo manejar los cambios de diseño para la desaprobación de auto_ptr en C ++ 11?

Estamos probando una biblioteca en C ++ 11 (es decir, -std=c++11). La biblioteca usa auto_ptr y este patrón:

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

C ++ 11 obsoleto auto_ptr, por lo que queremos alejarnos de él.

Sin embargo, el código admite tanto C ++ 03 como C ++ 11, por lo que no es tan simple como quitar auto_ptr. También vale la pena mencionar que la biblioteca no tiene dependencias externas. Usa C ++ 03; y no usa Autotools, Cmake, Boost, …

¿Cómo debemos manejar los cambios de diseño para alejarnos? de auto_ptr para C ++ 11 mientras se conserva la compatibilidad con C ++ 03?

Comentarios

  • ¿Alguno de los auto_ptr tiene ámbito (es decir, std::auto_ptr), es necesario o se puede obtener el puntero inteligente de algún otro espacio de nombres?
  • Como acotación al margen, le recomendamos que doble Foo::Initialize en Foo::Foo.
  • @ MSalters: sí, siempre ha sido una de esas cosas que me han incómodo un poco. La biblioteca se diseñó en la década de 1990 y creo que el diseño era similar al de MFC. Es decir, había menos construcción de nivel C ++, y luego una construcción de objeto " de nivel superior ". Creo que la función se usó como una compensación, por lo que las clases no ' t tiene 6 o 12 constructores diferentes. (En este punto, lo que he hecho es revisar y asegurarme de que las variables miembro de los tipos POD se inicialicen con los valores predeterminados adecuados en los constructores de C ++).

Respuesta

En la mayoría de los aspectos, el std::unique_ptr se hizo para ser incluido (pero más seguro) reemplazo para std::auto_ptr , por lo que debería haber muy pocos cambios de código (si es que se requieren alguno) que no sean (como preguntas) dirigiendo el código para que use unique_ptr o auto_ptr.

Hay algunas formas de hacerlo esto (y cada uno viene con su propia lista de compensaciones) a continuación. Dado el ejemplo de código proporcionado, preferiría cualquiera de las dos primeras opciones .

Opción 1

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

Compensaciones;

  • Introduce el nombre auto_ptr en el espacio de nombres global ; puede mitigar esto definiendo que es su propio " espacio de nombres " privado
  • Una vez que migre a C ++ 17 (Creo que auto_ptr se eliminará por completo). Puede buscar y reemplazar más fácilmente

Opción 2

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

Tradeoffs;

  • Probablemente sea más complicado trabajar con todos los auto_ptr actuales que deben cambiarse en el código para algo como my_ptr<T>::ptr
  • Mejor seguridad, los nombres no se introducen en el espacio de nombres global

Opción 3

Algo controvertido, pero si está preparado para tolerar las advertencias de tener una clase std como base

#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 

Compensaciones;

  • No intente usar la clase heredada donde se esperaría una base virtual (en particular, el destructor no virtual). No es que esto deba ser un problema en el caso – pero tenlo en cuenta
  • Nuevamente, cambios de código
  • Posibles discrepancias en el espacio de nombres; todo depende de cómo se use la clase de puntero para comenzar

Opción 4

Envuelva los punteros en una nueva clase y agregue las funciones requeridas al miembro

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

Compensaciones;

  • A un poco extremo cuando todo lo que realmente quieres es " intercambiar " las implementaciones

Comentarios

  • Muy buena respuesta. De hecho, lo investigué un poco y llegaste al menos a tres de las pruebas que intenté. (Lo que te falta son las cosas específicas de OS X y Clang. OS X es un oso porque todavía usa el espacio de nombres TR1 para C ++ 03 a veces, y debes incluir cosas usando este método: Ningún tipo llamado ' unique_ptr ' en el espacio de nombres ' std ' al compilar bajo LLVM / Clang ).
  • @jww. Yo ' m en OS X (XCode 6.4 y Apple LLVM versión 6.1.0 (clang-602.0.53) (basado en LLVM 3.6.0svn)) y no tengo problemas con C ++ 03/11 mezcla que no sea el espacio de nombres tr1 que ya no está allí (uso libc ++ y no libstdc ++).Sé que tr1 no era normativo, pero no puedo ' encontrar ningún lugar en el borrador (aquí) que los archivos tenían que ser <tr1/...> en absoluto, de hecho, menciona que solo están en el encabezado <memory>, etc., en el archivo tr1 espacio de nombres.
  • @jww. Supongo que dada una combinación particular de compilador, biblioteca y dispositivo de destino, es posible que deba hacer algunos soportes manuales más. De lo contrario, en OS X, considere pasar a clang y libc ++. Francamente, considero que libc ++ es la nueva biblioteca " nativa " de C ++ para OS X; yo lo usaría por defecto. No tengo forma de respaldar estas afirmaciones, aparte de que la historia de la relación clang / Apple y que las herramientas de GCC en OS X parecen desactualizadas (biblioteca) o simplemente eliminadas (hasta donde yo sé, GCC es un pequeño trozo de clang de todos modos ).
  • " De lo contrario, en OS X, considere pasar a clang y libc ++ … " – sí, estoy de acuerdo contigo. Sin embargo, nos gustaría dejar que los usuarios tomen esa decisión y no forzarlos. (Toman implícitamente la elección cuando especifican (o no) CXX=...).
  • Aquí ' s el caso eso me está causando tantos problemas en OS X 10.7 y 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. grep'd los directorios de inclusión que se volcaron, y no incluyen unique_ptr.

Respuesta

Opción 5: Alias directo.

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

Compensaciones:

  1. Para versiones de idiomas más recientes, también conocido como C ++ 11 y posteriores, su tipo de alias se asigna al puntero inteligente correcto. Cualquier código de usuario que realmente dependa de las API específicas de std :: auto_ptr será marcado por el compilador, lo cual es la máxima garantía de que realmente se arreglará.

  2. En Legacy En el modo c ++ 03, el alias de tipo es una macro. Esto es grosero, pero la sintaxis resultante MyPtr<T> será idéntica a la del caso C ++ 11 en el resto del código.

  3. Tienes que buscar y cambiar todas tus variables auto_ptr a MyPtr para configurar esto.

Comentarios

  • Es ' muy poco claro a qué se refiere esto (y, como está redactado, no es ' una pregunta).
  • @autophage Creo que es una respuesta … así que probablemente no sea una pregunta.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *