No código a seguir:
struct copy_only
{
copy_only() = default;
copy_only(const copy_only&) = default;
copy_only& operator=(const copy_only&) = default;
copy_only(copy_only&&) = delete;
copy_only& operator=(copy_only&&) = delete;
~copy_only() = default;
};
std::vector<copy_only> v;
copy_only c{};
v.push_back(c);
No MSVC obtemos o erro:
erro C2280: 'copy_only::copy_only(copy_only &&)': tentativa de referenciar uma função excluída
Isso vem de dentro da implementação do vetor, onde push_back(const&)
chama emplace_back
a implementação:
nota: ao compilar a função de membro do modelo de classe 'void std::vector<copy_only,std::allocator<copy_only>>::push_back(const _Ty &)'
nota: veja a referência à instanciação do modelo de função '_Ty &std::vector<_Ty,std::allocator<_Ty>>::_Emplace_one_at_back<const _Ty&>(const _Ty &)' sendo compilado
Isso compila com gcc e clang. Será que isso é apenas um bug enorme do compilador MSVC no exemplo de vetor mais simples? Ou existe algum padrão que impeça esse uso, algo que o gcc e o clang estão apenas ignorando?
TL;DR: Esta é uma aula mal escrita.
Classes copiáveis não devem resistir ativamente à movimentação, mesmo que não queiram ter nenhum comportamento de movimentação personalizado.
Em vez disso, eles deveriam deixar que a movimentação voltasse a ser cópia, o que acontece automaticamente se você não escrever nenhuma operação de movimentação, em vez de excluí-las explicitamente.
Isso não é algo que não funciona bem com
std::vector
. Isso causará problemas constantemente, porque ninguém espera que as classes façam isso.vector::push_back()
tem uma pré-condição de "T
é Cpp17CopyInsertable emX
", e Cpp17CopyInsertable implica Cpp17MoveInsertable de acordo com [container.alloc.reqmts-2.4] :Como sua classe não é Cpp17MoveInsertable e, portanto, não é Cpp17CopyInsertable , o que viola a pré-condição de
push_back()
e leva a um comportamento indefinido.O problema aqui é que funções excluídas explicitamente ainda são candidatas à resolução de sobrecarga, o que essencialmente faz com que
CopyInsertable
os requisitos falhem. Em vez disso, você pode simplesmente definir suas operações de cópia explicitamente e confiar no compilador para remover (ou seja, não gerar) as operações de movimentação, de forma que elas não sejam mais consideradas e as operações de cópia sejam usadas em seu lugar: