Na bela resposta ao idioma de copiar e trocar, há um código que eu preciso de ajuda:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
// ...
};
e ele adiciona uma nota
Há outras alegações de que devemos especializar std :: swap para o nosso tipo, fornecer uma troca em classe ao lado de uma troca de função livre, etc. Mas tudo isso é desnecessário: qualquer uso adequado de troca será por meio de uma chamada não qualificada , e nossa função será encontrada através da ADL. Uma função serve.
Com friend
eu sou um pouco em termos "hostis", devo admitir. Então, minhas principais perguntas são:
- parece uma função livre , mas está dentro do corpo da classe?
- por que isso não é
swap
estático ? Obviamente, ele não usa nenhuma variável de membro. - "Qualquer uso adequado do swap descobrirá o swap via ADL" ? A ADL pesquisará os namespaces, certo? Mas também olha dentro das classes? Ou é aqui que
friend
entra?
Perguntas secundárias:
- Com o C ++ 11, devo marcar meus
swap
s comnoexcept
? - Com C ++ 11 e sua gama-for , eu deveria colocar
friend iter begin()
efriend iter end()
da mesma forma dentro da classe? Eu acho quefriend
não é necessário aqui, certo?
c++
c++11
friend
copy-and-swap
towi
fonte
fonte
friend
função não é uma função membro?Respostas:
Existem várias maneiras de escrever
swap
, algumas melhores que outras. Com o tempo, porém, verificou-se que uma única definição funciona melhor. Vamos considerar como podemos pensar em escrever umaswap
função.Primeiro, vemos que contêineres
std::vector<>
possuem uma função de membro de argumento únicoswap
, como:Naturalmente, então, nossa classe também deveria, certo? Bem, na verdade não. A biblioteca padrão tem todo tipo de coisas desnecessárias e um membro
swap
é um deles. Por quê? Vamos continuar.O que devemos fazer é identificar o que é canônico e o que nossa classe precisa fazer para trabalhar com ela. E o método canônico de troca é com
std::swap
. É por isso que as funções membro não são úteis: elas não são como devemos trocar as coisas, em geral, e não têm influência no comportamento destd::swap
.Bem, então, para fazer o
std::swap
trabalho, devemos fornecer (estd::vector<>
deveria ter fornecido) uma especializaçãostd::swap
, certo?Bem, isso certamente funcionaria neste caso, mas tem um problema evidente: as especializações de funções não podem ser parciais. Ou seja, não podemos especializar classes de modelo com isso, apenas instanciações específicas:
Esse método funciona algumas vezes, mas não o tempo todo. Deve haver uma maneira melhor.
Há sim! Podemos usar uma
friend
função e encontrá-la através da ADL :Quando queremos trocar alguma coisa, associamos †
std::swap
e, em seguida, fazemos uma chamada não qualificada:O que é uma
friend
função? Há confusão nessa área.Antes da padronização do C ++, as
friend
funções executavam algo chamado "injeção de nome de amigo", em que o código se comportava como se a função tivesse sido escrita no espaço para nome ao redor. Por exemplo, esses eram pré-padrão equivalente:No entanto, quando a ADL foi inventada, essa foi removida. A
friend
função só poderia ser encontrada via ADL; se você a quisesse como uma função livre, ela precisava ser declarada como tal ( veja isso , por exemplo). Mas eis! Havia um problema.Se você apenas usar
std::swap(x, y)
, sua sobrecarga nunca será encontrada, porque você disse explicitamente "procure dentrostd
e em nenhum outro lugar"! É por isso que algumas pessoas sugeriram escrever duas funções: uma como uma função que pode ser encontrada via ADL e a outra para lidar comstd::
qualificações explícitas .Mas, como vimos, isso não pode funcionar em todos os casos, e acabamos com uma bagunça feia. Em vez disso, a troca idiomática seguiu o outro caminho: em vez de tornar o trabalho das classes fornecer
std::swap
, é trabalho dos trocadores garantir que eles não usem qualificadaswap
, como acima. E isso tende a funcionar muito bem, desde que as pessoas saibam disso. Mas aí está o problema: não é intuitivo precisar usar uma chamada não qualificada!Para facilitar isso, algumas bibliotecas como o Boost forneceram a função
boost::swap
, que apenas faz uma chamada não qualificadaswap
, comstd::swap
um espaço de nome associado. Isso ajuda a tornar as coisas sucintas novamente, mas ainda é uma chatice.Observe que não há alteração no C ++ 11 no comportamento de
std::swap
, que eu e outros pensamos erroneamente que seria o caso. Se você foi mordido por isso, leia aqui .Em resumo: a função membro é apenas ruído, a especialização é feia e incompleta, mas a
friend
função está completa e funciona. E quando você trocar, useboost::swap
ou um não qualificadoswap
comstd::swap
associado.† Informalmente, um nome é associado se for considerado durante uma chamada de função. Para detalhes, leia §3.4.2. Nesse caso,
std::swap
normalmente não é considerado; mas podemos associá- lo (adicione-o ao conjunto de sobrecargas consideradas não qualificadasswap
), permitindo que seja encontrado.fonte
std::vector<std::string>().swap(someVecWithData);
, o que não é possível com umaswap
função livre porque os dois argumentos são passados por referência não const.operator=
,operator+
eoperator+=
, mas é evidente que os operadores em classes relevantes são aceitos / esperados de existir para simetria. O mesmo vale para oswap
escopo de membro + espaço para nomeswap
na minha opinião.function<void(A*)> f; if(!f) { }
pode falhar apenas porqueA
declara umoperator!
que aceitaf
igualmente bemf
o própriooperator!
(improvável, mas pode acontecer). Sefunction<>
o autor pensasse "ohh, eu tenho um 'operador bool' ', por que eu deveria implementar' operator! '? Isso violaria DRY!", Isso seria fatal. Você só precisa ter umoperator!
implementado paraA
, eA
ter um construtor para afunction<...>
, e as coisas vão quebrar, porque os dois candidatos exigirão conversões definidas pelo usuário.Esse código é equivalente (em quase todos os aspectos) a:
Uma função de amigo definida dentro de uma classe é:
inline
As regras exatas estão na seção
[class.friend]
(cito os parágrafos 6 e 7 do rascunho de C ++ 0x):fonte
swap
visível apenas para ADL. É um membro do espaço para nome anexo, mas seu nome não é visível para os outros formulários de pesquisa de nome. EDIT: Eu vejo que @GMan foi mais rápido novamente :) @ Ben sempre foi assim no ISO C ++ :)friend
funções são encontradas apenas pela ADL e, se elas precisam ser apenas funções livres comfriend
acesso, elas precisam ser declaradas comofriend
dentro da classe e como uma declaração de função livre normal fora da classe. Você pode ver essa necessidade nesta resposta , por exemplo.