std::swap()
é usado por muitos contêineres std (como std::list
e std::vector
) durante a classificação e até mesmo a atribuição.
Mas a implementação padrão do swap()
é muito generalizada e bastante ineficiente para tipos personalizados.
Assim, a eficiência pode ser obtida sobrecarregando std::swap()
com uma implementação específica de tipo personalizado. Mas como você pode implementá-lo para que seja usado pelos contêineres std?
Respostas:
A maneira certa de sobrecarregar a troca é gravá-la no mesmo namespace do que você está trocando, para que possa ser encontrada por meio de pesquisa dependente de argumento (ADL) . Uma coisa particularmente fácil de fazer é:
fonte
std::sort
que usa ADL para trocar elementos não está em conformidade com C ++ 03, mas está em conformidade com C ++ 11. Além disso, por que -1 uma resposta baseada no fato de que os clientes podem usar código não idiomático?Atenção Mozza314
Aqui está uma simulação dos efeitos de uma
std::algorithm
chamada genéricastd::swap
e fazendo com que o usuário forneça sua troca no namespace std. Como se trata de um experimento, esta simulação usa emnamespace exp
vez denamespace std
.Para mim, isso imprime:
Se o seu compilador imprimir algo diferente, ele não está implementando corretamente a "pesquisa em duas fases" para modelos.
Se o seu compilador estiver em conformidade (com qualquer um dos C ++ 98/03/11), ele fornecerá a mesma saída que mostrei. E, nesse caso, exatamente o que você teme que aconteça, realmente acontece. E colocar seu
swap
no namespacestd
(exp
) não impediu que isso acontecesse.Dave e eu somos membros do comitê e temos trabalhado nessa área do padrão por uma década (e nem sempre de acordo um com o outro). Mas essa questão foi resolvida há muito tempo, e ambos concordamos em como isso foi resolvido. Ignore a opinião / resposta de especialista de Dave nesta área por sua própria conta e risco.
Este problema veio à tona após a publicação do C ++ 98. A partir de 2001, Dave e eu começamos a trabalhar nessa área . E esta é a solução moderna:
O resultado é:
Atualizar
Foi feita uma observação que:
trabalho! Então, por que não usar isso?
Considere o caso em que seu
A
é um modelo de classe:Agora não funciona novamente. :-(
Portanto, você pode
swap
inserir o namespace std e fazê-lo funcionar. Mas você precisa se lembrar de colocarswap
noA
namespace 's para o caso quando você tem um modelo:A<T>
. E uma vez que ambos os casos irá funcionar se você colocarswap
emA
's namespace, é apenas mais fácil de lembrar (e para ensinar aos outros) para fazê-lo apenas que uma maneira.fonte
template <>
seu primeiro exemplo, obtenho a saídaexp::swap(A, A)
do gcc. Então, por que não preferir a especialização?using std::swap
é uma exceção à regra "nunca coloque instruções usando dentro de arquivos de cabeçalho"? Na verdade, por que não colocarusing std::swap
dentro<algorithm>
? Suponho que isso poderia quebrar uma pequena minoria do código das pessoas. Talvez descontinuar o suporte e, eventualmente, incluí-lo?using std::swap
escopo da função dentro de seus cabeçalhos. Sim,swap
é quase uma palavra-chave. Mas não, não é bem uma palavra-chave. Portanto, é melhor não exportá-lo para todos os namespaces até que seja realmente necessário.swap
é muito parecidooperator==
. A maior diferença é que ninguém nunca pensa em chamaroperator==
com sintaxe de namespace qualificada (seria muito feio).Você não tem permissão (pelo padrão C ++) para sobrecarregar std :: swap, no entanto, você está especificamente autorizado a adicionar especializações de modelo para seus próprios tipos ao namespace std. Por exemplo
então, os usos nos contêineres std (e em qualquer outro lugar) escolherão sua especialização em vez da geral.
Observe também que fornecer uma implementação de classe base de swap não é bom o suficiente para seus tipos derivados. Por exemplo, se você tem
isto funcionará para classes Base, mas se você tentar trocar dois objetos Derivados ele usará a versão genérica de std porque a troca modelada é uma correspondência exata (e evita o problema de trocar apenas as partes 'base' de seus objetos derivados )
NOTA: Eu atualizei isso para remover as partes erradas da minha última resposta. D'oh! (obrigado puetzk e j_random_hacker por apontar isso)
fonte
Embora seja correto que não se deva geralmente adicionar coisas ao std :: namespace, adicionar especializações de template para tipos definidos pelo usuário é especificamente permitido. Sobrecarregar as funções, não. Esta é uma diferença sutil :-)
Uma especialização de std :: swap ficaria assim:
Sem o bit template <>, seria uma sobrecarga, que é indefinida, ao invés de uma especialização, que é permitida. A abordagem sugerida de @Wilka para alterar o espaço de nomes padrão pode funcionar com o código do usuário (devido à pesquisa de Koenig preferir a versão sem espaço de nomes), mas não é garantido, e de fato não deveria funcionar (a implementação de STL deve usar o -qualificado std :: swap).
Há um tópico em comp.lang.c ++. Moderado com uma longa discussão sobre o assunto. A maior parte é sobre especialização parcial (o que atualmente não existe uma boa maneira de fazer).
fonte
vector
::swap
sobrecarga que você adicionou é mais especializada do que astd::swap
sobrecarga paravector
, portanto, ela captura a chamada e nenhuma especialização da última é relevante. Não tenho certeza de como isso é um problema prático (mas também não estou afirmando que seja uma boa ideia!).