Comment gérer les modifications de conception pour la dépréciation auto_ptr en C ++ 11?

12

Nous testons une bibliothèque sous C ++ 11 (ie, -std=c++11). La bibliothèque utilise auto_ptret ce modèle:

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

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

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

    return ptr.release();
}

C ++ 11 déconseillé auto_ptr, nous voulons donc nous en éloigner.

Cependant, le code prend en charge à la fois C ++ 03 et C ++ 11, ce n'est donc pas aussi simple que de tirer auto_ptr. Il convient également de mentionner que la bibliothèque n'a pas de dépendances externes. Il utilise C ++ 03; et n'utilise pas Autotools, Cmake, Boost, ...

Comment gérer les modifications de conception pour s'éloigner de auto_ptrC ++ 11 tout en conservant la compatibilité avec C ++ 03?

MetaFight
la source
Existe-t-il des auto_ptrzones de portée (c.-à-d. std::auto_ptr), Doivent- elles l'être ou le pointeur intelligent peut-il être obtenu à partir d'un autre espace de noms?
Niall
En aparté, vous pouvez plier Foo::Initializeen Foo::Foo.
MSalters
1
@MSalters - oui, cela a toujours été une de ces choses pour lesquelles je me suis senti légèrement mal à l'aise. La bibliothèque a été conçue dans les années 1990, et je pense que la conception était similaire à MFC. Autrement dit, il y avait une construction C ++ de niveau inférieur, puis une construction d'objet "de niveau supérieur". Je pense que la fonctionnalité a été utilisée comme compromis afin que les classes n'aient pas 6 ou 12 constructeurs différents. (À ce stade, ce que j'ai fait est passé en revue et m'a assuré que les variables membres des types POD sont initialisées à des valeurs par défaut saines dans les constructeurs C ++).

Réponses:

13

À la plupart des égards, le std::unique_ptrremplacement a été fait (mais plus sûr) std::auto_ptr, donc il ne devrait y avoir que très peu (le cas échéant) de modifications de code autres que (comme vous le demandez) de demander au code d'utiliser soit unique_ptrou auto_ptr.

Il y a quelques façons de le faire (et chacune vient avec sa propre liste de compromis) ci-dessous. Compte tenu de l'exemple de code fourni, je préférerais l'une des deux premières options .

Option 1

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

Compromis;

  • Vous introduisez le auto_ptrnom dans l'espace de noms global; vous pouvez atténuer cela en le définissant comme votre propre espace de noms "privé"
  • Une fois migré vers C ++ 17 (je pense auto_ptrqu'il sera complètement supprimé), vous pouvez plus facilement rechercher et remplacer

Option 2

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

Compromis;

  • Probablement plus lourd à travailler, tout le auto_ptrbesoin actuel de changer dans le code pour quelque chose commemy_ptr<T>::ptr
  • Meilleure sécurité, les noms ne sont pas introduits dans l'espace de noms global

Option 3

Un peu controversé, mais si vous êtes prêt à accepter les mises en garde d'avoir une stdclasse comme 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

Compromis;

  • N'essayez pas d'utiliser la classe héritée où une base virtuelle (en particulier avec le destructeur non virtuel) serait attendue. Non pas que cela devrait être un problème dans le cas - mais sachez-le
  • Encore une fois, le code change
  • Inadéquations potentielles de l'espace de noms - tout dépend de la façon dont la classe de pointeur est utilisée pour commencer

Option 4

Envelopper les pointeurs dans une nouvelle classe et agréger les fonctions requises au membre

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

Compromis;

  • Un peu extrême quand tout ce que vous voulez, c'est "échanger" les implémentations
Niall
la source
Très bonne réponse. En fait, j'ai fait des recherches un peu et vous avez réussi au moins trois des tests que j'ai essayés. (Ce qui vous manque, ce sont les choses spécifiques à OS X et Clang. OS X est un ours car il utilise toujours l'espace de noms TR1 pour C ++ 03, et vous devez inclure des choses en utilisant cette méthode: Aucun type nommé 'unique_ptr' dans l'espace de noms 'std' lors de la compilation sous LLVM / Clang ).
@jww. Je suis sous OS X (XCode 6.4 et Apple LLVM version 6.1.0 (clang-602.0.53) (basé sur LLVM 3.6.0svn)) et je n'ai aucun problème avec le mixage C ++ 03/11 autre que l' tr1espace de noms non étant plus là (j'utilise libc ++ et non libstdc ++). Je sais que tr1 n'était pas normatif, mais je ne trouve nulle part dans le brouillon (ici) que les fichiers devaient être <tr1/...>du tout, en fait il mentionne simplement être dans le <memory>fichier d' en-tête, etc. juste dans l' tr1espace de noms.
Niall
@jww. Je suppose que, étant donné un mélange particulier de compilateur, de bibliothèque et de périphérique cible - vous devrez peut-être faire quelques supports de plus. Sinon, sur OS X, envisagez de passer à clang et libc ++. Franchement, je considère que la libc ++ est la nouvelle bibliothèque C ++ "native" pour OS X - je serais par défaut à cela. Je n'ai aucun moyen de soutenir ces affirmations, à part que l'histoire de la relation clang / Apple et que les outils GCC sur OS X semblent obsolètes (bibliothèque) ou tout simplement supprimés (pour autant que je sache, GCC est un mince bout à cliqueter de toute façon ).
Niall
"Sinon, sur OS X, envisagez de passer à clang et libc ++ ..." - ouais, je suis en quelque sorte d'accord avec vous. Cependant, nous aimerions laisser les utilisateurs faire ce choix et ne pas le leur imposer. (Ils font implicitement le choix lorsqu'ils précisent (ou manquent) CXX=...).
Voici le cas qui me cause tant de problèmes sur OS X 10.7 et 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. J'inclus grep'dles répertoires qui ont été vidés, et ils ne comprennent pasunique_ptr .
0

Option 5: alias direct.

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

Compromis:

  1. Pour les versions de langage plus récentes, AKA C ++ 11 et versions ultérieures, votre type d'alias correspond au pointeur intelligent approprié. Tout code utilisateur qui dépend en fait des API spécifiques à std :: auto_ptr sera signalé par le compilateur, ce qui est la garantie ultime qu'il sera vraiment corrigé.

  2. En mode Legacy c ++ 03, l'alias de type est une macro. C'est brut, mais la syntaxe résultante MyPtr<T>sera identique au cas C ++ 11 dans le reste du code.

  3. Vous devez trouver et modifier toutes vos variables auto_ptr MyPtrafin de configurer cela.

user3726672
la source
1
On ne sait pas très bien à quoi cela fait référence (et, tel que formulé, ce n'est pas du tout une question).
autophage
1
@autophage Je crois que c'est une réponse ... donc probablement pas une question.
Kain0_0