Por que é errado usar std :: auto_ptr <> com contêineres padrão?

217

Por que é errado usar std::auto_ptr<>com contêineres padrão?

Uhall
fonte
5
Definitivamente um +1 nisso, porque eu já vi tantas pessoas entenderem isso errado. É uma ótima pergunta a fazer.
Twokats
Por favor, leia também o item relacionado. Esta questão é considerada aqui do outro lado. Pode ser útil entender mais sobre os contêineres auto_ptr e STL. stackoverflow.com/questions/8630552/…
nickolay
1
movesemântica e unique_ptrforam projetados para evitar os problemas relacionados a auto_ptr. No C ++ 03, a linguagem não era poderosa o suficiente para escrever uma classe como auto_ptraquela que se comporta corretamente e com segurança em todos os cenários, pois o compilador e a linguagem não conseguiram distinguir os valores de l e r, de modo que alguns "hacks" foram usados ​​para obter o comportamento desejado a maior parte do tempo.
Phil1970
Artigo agradável: STL Containers e Auto_ptrs - Por que eles não misturam quantstart.com/articles/…
alfC

Respostas:

124

O padrão C ++ diz que um elemento STL deve ser "cópia construtível" e "atribuível". Em outras palavras, um elemento deve poder ser atribuído ou copiado e os dois elementos são logicamente independentes. std::auto_ptrnão cumpre esse requisito.

Tomemos por exemplo este código:

class X
{
};

std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);

std::auto_ptr<X> pX = vecX[0];  // vecX[0] is assigned NULL.

Para superar essa limitação, você deve usar o std::unique_ptr, std::shared_ptrou std::weak_ptrponteiros inteligentes ou o impulso equivalentes, se você não tem C ++ 11. Aqui está a documentação da biblioteca de impulso para esses ponteiros inteligentes.

Kevin
fonte
7
Você também deve considerar os contêineres do ponteiro de aumento, se não precisar de propriedade compartilhada.
me22 10/10/09
4
unique_ptrtambém não permite copiar, portanto, certas operações STL não funcionarão corretamente, a menos que possam usar sua semântica de movimentação.
9133 Mike Weller
4
"Para superar essa limitação, você deve usar o std::unique_ptr": esse modelo de classe só pode existir por causa da semântica de movimentação (sua especificação requer referências de rvalue), portanto, fundamentalmente, requer C ++ 11. No entanto (e relacionado), o C ++ 11 Standard não diz mais que um tipo de elemento STL deve ser "copiável-construtível" e "atribuível"; ser move-construtível e move-designável é suficiente. De fato, as unique_ptrinstâncias são apenas construtivas e atribuíveis a movimentação. Mas também são auto_ptrexemplos! Como conseqüência, no C ++ 11, você pode fazer auto_ptro que pode fazer unique_ptr.
Marc van Leeuwen
@MarcvanLeeuwen a menos que você resete releasequando necessário
catraca aberração
2
@ratchetfreak: Hmm, eu não entendo. O que? "a menos que você resete release", não vejo como isso se aplica a nada no meu comentário. Observe que ambos auto_ptre unique_ptrpossuem esses dois métodos, e eles fazem a mesma coisa nos dois casos.
Marc van Leeuwen
66

A semântica de cópia de auto_ptrnão é compatível com os contêineres.

Especificamente, copiar um auto_ptrpara o outro não cria dois objetos iguais, pois um deles perdeu a propriedade do ponteiro.

Mais especificamente, copiar um auto_ptrfaz com que uma das cópias solte o ponteiro. Qual desses restos no contêiner não está definido. Portanto, você pode perder aleatoriamente o acesso aos ponteiros se armazenar auto_ptrsnos contêineres.

Frank Krueger
fonte
39

Dois artigos super excelentes sobre o assunto:

lazer
fonte
Porque acho que, nos próximos dois anos, ele provavelmente lidou com o problema em questão.
Filhote de cachorro
27
@DeadMG: sim, você está correto. Mas esse não era meu objetivo. Se alguém chegar a esse tópico em algum momento e quiser aprender sobre auto_ptressas coisas, esses links serão úteis, tenho certeza.
Lazer
Há muitas duplicatas mais recentes.
Filhote
8
@DeadMG: Esta questão não foi encerrada como duplicada e, portanto, está aberta para extensão. Lazer disse o que não foi dito antes aqui. Eu acho que ele veio por acaso.
Sebastian Mach
As explicações no segundo link, que analisam o problema após a chamada sort(), são mais claras do que todas as respostas aqui.
chaosink
17

Os contêineres da STL precisam ser capazes de copiar os itens armazenados neles e foram projetados para esperar que o original e a cópia sejam equivalentes. objetos de ponteiro automático têm um contrato completamente diferente, pelo qual a cópia cria uma transferência de propriedade. Isso significa que os contêineres de auto_ptr exibirão um comportamento estranho, dependendo do uso.

Há uma descrição detalhada do que pode dar errado no item 8 do STL eficaz (Scott Meyers) e também uma descrição não tão detalhada no item 13 do C ++ eficaz (Scott Meyers).

Garth Gilmour
fonte
12

Os contêineres STL armazenam cópias dos itens contidos. Quando um auto_ptr é copiado, ele define o ptr antigo como nulo. Muitos métodos de contêiner são quebrados por esse comportamento.

Dustin Getz
fonte
Mas, ao usar unique_ptr, você obtém praticamente a mesma coisa, pois apenas um unique_ptr pode ter a propriedade do objeto?
Tracer
2
O @Tracer, unique_ptrcomo qualquer objeto C ++ 11 adequado, só pode transferir a propriedade de seu recurso quando construído ou movido, movendo-o, garantindo que o programador passe deliberadamente um std::move(sourceObject)ou um temporário, em vez de passar um lvalue e, de maneira não intuitiva / imprevisível, alterando-o por a tarefa de cópia ... que, como enfatizado aqui, era um problema central auto_ptr.
underscore_d
4

A norma C ++ 03 (ISO-IEC 14882-2003) diz na cláusula 20.4.5, parágrafo 3:

[...] [ Nota: [...] auto_ptr não atende aos requisitos de CopyConstructible e Assignable para elementos de contêiner da Biblioteca Padrão e, assim, instanciar um contêiner da Biblioteca Padrão com um auto_ptr resulta em comportamento indefinido. - nota final ]

A norma C ++ 11 (ISO-IEC 14882-2011) diz no apêndice D.10.1, parágrafo 3:

[...] Nota: [...] Instâncias de auto_ptr atender os requisitos da MoveConstructible e MoveAssignable, mas não atender aos requisitos de CopyConstructible e CopyAssignable. - nota final]

A norma C ++ 14 (ISO-IEC 14882-2014) diz no apêndice C.4.2 Anexo D: recursos de compatibilidade:

Alteração : Os modelos de classe auto_ptr, unary_function e binary_function, os modelos de função random_shuffle e os modelos de função (e seus tipos de retorno) ptr_fun, mem_fun, mem_fun_ref, bind1st e bind2nd não estão definidos.
Justificativa : Substituída por novos recursos.
Efeito no recurso original : O código C ++ 2014 válido que usa esses modelos de classe e modelos de função pode falhar na compilação neste Padrão Internacional.

bitek
fonte