Como lidar com as alterações de design para a depreciação do auto_ptr no C ++ 11?

Estamos testando uma biblioteca em C ++ 11 (ou seja, -std=c++11). A biblioteca usa auto_ptr e este padrão:

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, então queremos nos afastar disso.

No entanto, o código suporta C ++ 03 e C ++ 11, portanto, não é tão simples quanto arrancar auto_ptr. Também vale a pena mencionar que a biblioteca não tem dependências externas. Ela usa C ++ 03; e não usa Autotools, Cmake, Boost, …

Como devemos lidar com as mudanças de design para sair de auto_ptr para C ++ 11 enquanto mantém a compatibilidade com C ++ 03?

Comentários

  • Algum dos auto_ptr escopo (isto é, std::auto_ptr), eles precisam ser ou o ponteiro inteligente pode ser obtido de algum outro namespace?
  • Como um aparte, você pode querer dobrar Foo::Initialize em Foo::Foo.
  • @ MSalters – sim, sempre foi uma daquelas coisas que me deixou um pouco desconfortável. A biblioteca foi projetada na década de 1990 e acho que o projeto era semelhante ao MFC. Ou seja, havia menos construção de nível C ++ e, em seguida, uma construção de objeto " nível superior ". Acho que o recurso foi usado como uma compensação para que as classes não ' t tem 6 ou 12 construtores diferentes. (Neste ponto, o que eu fiz foi realizado e garantido que as variáveis de membro dos tipos POD sejam inicializadas para padrões sãos nos construtores C ++).

Resposta

Na maioria dos aspectos, o std::unique_ptr foi feito para ser entregue (mas mais seguro) substituição para std::auto_ptr , então deve haver muito poucas (se houver) alterações de código necessárias além (como você pede) direcionando o código para usar unique_ptr ou auto_ptr.

Existem algumas maneiras de fazer isso (e cada um vem com suas próprias compensações de lista) abaixo. Dado o exemplo de código fornecido, Eu preferiria qualquer uma das duas primeiras opções .

Opção 1

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

Trocas;

  • Você introduz o nome auto_ptr no namespace global ; você pode atenuar isso definindo que é seu próprio " privado " namespace
  • Depois de migrar para C ++ 17 (Eu acredito que auto_ptr será completamente removido) você pode pesquisar e substituir mais facilmente

Opção 2

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

Compensações;

  • Provavelmente mais complicado de se trabalhar, todas as auto_ptr atuais precisam ser alteradas no código para algo como my_ptr<T>::ptr
  • Melhor segurança porque os nomes não estão sendo introduzidos no namespace global

Opção 3

Um pouco controverso, mas se você estiver preparado para tolerar as advertências de ter uma std classe 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 

Tradeoffs;

  • Não tente usar a classe herdada onde uma base virtual (em particular, o destruidor não virtual) seria esperada. Não que isso deva ser um problema no caso – mas esteja ciente disso
  • Novamente, alterações de código
  • Possíveis incompatibilidades de namespace – tudo depende de como a classe de ponteiro é usada para começar

Opção 4

Envolva os ponteiros em uma nova classe e agregue as funções necessárias ao membro

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

Trocas;

  • A um pouco extremo quando tudo o que você realmente deseja é " trocar " as implementações fora

Comentários

  • Muito boa resposta. Na verdade, pesquisei um pouco e você acertou pelo menos três dos testes que tentei. (O que falta são as coisas específicas do OS X e do Clang. OS X é um urso porque às vezes ainda usa o namespace TR1 para C ++ 03, e você deve incluir coisas usando este método: Nenhum tipo denominado ' unique_ptr ' no namespace ' std ' ao compilar em LLVM / Clang ).
  • @jww. I ' m no OS X (XCode 6.4 e Apple LLVM versão 6.1.0 (clang-602.0.53) (baseado no LLVM 3.6.0svn)) e não tenho problemas com o C ++ 03/11 mix diferente do tr1 namespace não existe mais (eu uso libc ++ e não libstdc ++).Eu sei que tr1 não era normativo, mas não posso ' encontrar em qualquer lugar do rascunho (aqui) que o os arquivos precisavam ser <tr1/...>, na verdade, ele menciona apenas estar no cabeçalho <memory> etc. arquivo apenas no tr1 namespace.
  • @jww. Eu acho que dada uma combinação particular de compilador, biblioteca e dispositivo de destino – você pode precisar fazer mais alguns suportes manuais. Caso contrário, no OS X, considere mudar para clang e libc ++. Sinceramente, considero a libc ++ a nova " nativa " biblioteca C ++ para OS X – isso seria o padrão. Não tenho como apoiar essas afirmações de que a história do relacionamento clang / Apple e que as ferramentas do GCC no OS X parecem desatualizadas (biblioteca) ou apenas removidas (pelo que eu sei o GCC é um toco para clang de qualquer maneira ).
  • " Caso contrário, no OS X, considere mudar para clang e libc ++ … " – sim, eu meio que concordo com você. No entanto, gostaríamos de permitir que os usuários façam essa escolha, e não forçá-los. (Eles implicitamente fazem a escolha quando especificam (ou faltam) CXX=...).
  • Aqui ' é o caso isso está me causando tantos problemas no OS X 10.7 e 10.8: c++ -v -std=c++11 -x c++ - < /dev/null. Eu grep'd os diretórios incluídos que foram despejados, e eles não incluem unique_ptr.

Resposta

Opção 5: alias direto.

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

Trocas:

  1. Para versões de idioma mais recentes, também conhecido como C ++ 11 e posterior, seu tipo de alias mapeia para o ponteiro inteligente correto. Qualquer código de usuário que realmente dependa de APIs específicas para std :: auto_ptr será sinalizado pelo compilador, o que é a garantia definitiva de que “será realmente corrigido.

  2. No Legacy No modo c ++ 03, o alias do tipo é uma macro. Isso é bruto, mas a sintaxe resultante MyPtr<T> será idêntica ao caso C ++ 11 em todo o resto do código.

  3. Você deve encontrar e alterar todas as suas variáveis auto_ptr para MyPtr para configurar isso.

Comentários

  • ' é muito obscuro o que isso está referenciando (e, como expresso, não é ' uma pergunta).
  • @autophage Acredito que seja uma resposta … então provavelmente não é uma pergunta.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *