¿Cómo manejar los cambios de diseño para la eliminación automática de auto_ptr en C ++ 11?

12

Estamos probando una biblioteca en C ++ 11 (es decir, -std=c++11). La biblioteca usa auto_ptry 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 en desuso auto_ptr, por lo que queremos alejarnos de él.

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

¿Cómo deberíamos manejar los cambios de diseño para alejarnos de auto_ptrC ++ 11 mientras conservamos la compatibilidad con C ++ 03?

MetaFight
fuente
¿ auto_ptrAlguno de los ámbitos (es decir std::auto_ptr), ¿deben ser o se puede obtener el puntero inteligente de algún otro espacio de nombres?
Niall
Como acotación al margen, es posible que desee para doblar Foo::Initializeen Foo::Foo.
MSalters
1
@MSalters: sí, esa ha sido siempre una de esas cosas por las que me he sentido un poco incómoda. La biblioteca fue diseñada en la década de 1990, y creo que el diseño era similar a MFC. Es decir, hubo una construcción de C ++ de nivel inferior, y luego una construcción de objeto de "nivel superior". Creo que la característica se usó como una compensación, por lo que las clases no tienen 6 o 12 constructores diferentes. (En este punto, lo que he hecho ha pasado y se ha asegurado de que las variables miembro de los tipos de POD se inicialicen con valores predeterminados correctos en los constructores de C ++).

Respuestas:

13

En la mayoría de los aspectos, el std::unique_ptrfue hecho para ser gota en el reemplazo (pero más seguro) para std::auto_ptr, por lo que debe ser muy pocos (si los hay) los cambios de código requiere que no sea (como se le pregunte) dirigir el código para utilizar cualquiera unique_ptro auto_ptr.

Hay algunas maneras de hacer esto (y cada una viene con sus propias compensaciones de lista) 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 auto_ptrnombre 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 auto_ptrque se eliminará por completo), puede buscar y reemplazar más fácilmente

opcion 2

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

Compensaciones;

  • Probablemente más engorroso para trabajar, toda la auto_ptrnecesidad actual de cambiar en el código a algo comomy_ptr<T>::ptr
  • Mejor seguridad: los nombres no se están introduciendo en el espacio de nombres global

Opción 3

Algo controvertido, pero si estás preparado para soportar las advertencias de tener una stdclase 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 utilizar la clase heredada donde se esperaría una base virtual (en particular, wrt, el destructor no virtual). No es que esto deba ser un problema en el caso, pero ten en cuenta
  • De nuevo, el código cambia
  • Posibles desajustes del espacio de nombres: todo depende de cómo se use la clase de puntero para empezar

Opcion 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;

  • Un poco extremo cuando todo lo que realmente quieres es "intercambiar" las implementaciones
Niall
fuente
Muy buena respuesta. De hecho, lo investigué un poco, y obtuviste al menos tres de las pruebas que probé. (Lo que le 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 debe incluir cosas usando este método: ningún tipo llamado 'unique_ptr' en el espacio de nombres 'std' al compilar bajo LLVM / Clang ).
@jww. Estoy 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 la mezcla C ++ 03/11 que no sea el tr1espacio de nombres no ya no estoy allí (uso libc ++ y no libstdc ++). Sé que tr1 no era normativo, pero no puedo encontrar en ninguna parte del borrador (aquí) que los archivos tenían que estar <tr1/...>en absoluto, de hecho menciona que solo está en el encabezado, <memory>etc., solo en el tr1espacio de nombres.
Niall
@jww. Supongo que dada una combinación particular de compilador, biblioteca y dispositivo de destino, es posible que tenga que hacer algunos soportes manuales más. De lo contrario, en OS X, considere pasar a clang y libc ++. Francamente, considero que el libc ++ es la nueva biblioteca "nativa" de C ++ para OS X, de manera predeterminada. No tengo forma de respaldar estas afirmaciones, aparte de la historia de la relación clang / Apple y que las herramientas de GCC en OS X parecen estar desactualizadas (biblioteca) o simplemente eliminadas (hasta donde sé, GCC es un trozo delgado para resonar de todos modos )
Niall
"De lo contrario, en OS X, considera pasar a clang y libc ++ ..." - sí, estoy de acuerdo contigo. Sin embargo, nos gustaría permitir que los usuarios hagan esa elección, y no forzarla. (Implícitamente toman la decisión cuando especifican (o carecen) CXX=...).
Aquí está el caso que me está causando tantos problemas en OS X 10.7 y 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Yo grep'dincluyo los directorios que fueron volcados, y no incluyen unique_ptr.
0

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, AKA C ++ 11 y posteriores, su tipo de alias se asigna al puntero inteligente correcto. El compilador marcará cualquier código de usuario que realmente dependa de las API específicas de std :: auto_ptr, que es la máxima garantía de que realmente se solucionará.

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

  3. Tiene que encontrar y cambiar todas sus variables auto_ptr MyPtrpara configurar esto.

usuario3726672
fuente
1
No está muy claro a qué se refiere esto (y, como está dicho, no es una pregunta en absoluto).
Autofagia
1
@autophage Creo que es una respuesta ... así que probablemente no sea una pregunta.
Kain0_0