En la hermosa respuesta al idioma de copiar e intercambiar hay un código que necesito un poco de ayuda:
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);
}
// ...
};
y agrega una nota
Hay otras afirmaciones de que deberíamos especializarnos en std :: swap para nuestro tipo, proporcionar un intercambio en clase junto con un intercambio de función libre, etc. Pero esto es innecesario: cualquier uso adecuado de swap se realizará a través de una llamada no calificada , y nuestra función se encontrará a través de ADL. Una función servirá.
Con friendque estoy un poco en términos "hostiles", debo admitirlo. Entonces, mis preguntas principales son:
- parece una función libre , pero está dentro del cuerpo de la clase?
- ¿Por qué no es esto
swapestático ? Obviamente no usa ninguna variable miembro. - "¿Cualquier uso apropiado de swap lo descubrirá a través de ADL" ? ADL buscará los espacios de nombres, ¿verdad? ¿Pero también se ve dentro de las clases? ¿O es aquí donde
friendentra?
Preguntas secundarias:
- Con C ++ 11, ¿debo marcar mi
swaps connoexcept? - Con C ++ 11 y su rango-para , debo colocar
friend iter begin()yfriend iter end()de la misma manera dentro de la clase? Creo quefriendno se necesita aquí, ¿verdad?
c++
c++11
friend
copy-and-swap
Towi
fuente
fuente

friendfunción no es una función miembro en absoluto.Respuestas:
Hay varias formas de escribir
swap, algunas mejores que otras. Sin embargo, con el tiempo, se descubrió que una sola definición funciona mejor. Consideremos cómo podríamos pensar en escribir unaswapfunción.Primero vemos que los contenedores como
std::vector<>tienen una función miembro de argumento únicoswap, como:Naturalmente, entonces, nuestra clase también debería, ¿verdad? Bueno en realidad no. La biblioteca estándar tiene todo tipo de cosas innecesarias , y un miembro
swapes una de ellas. ¿Por qué? Continúemos.Lo que debemos hacer es identificar qué es canónico y qué necesita hacer nuestra clase para trabajar con él. Y el método canónico de intercambio es con
std::swap. Esta es la razón por la cual las funciones miembro no son útiles: no son cómo debemos intercambiar cosas, en general, y no influyen en el comportamiento destd::swap.Bueno, para hacer el
std::swaptrabajo deberíamos proporcionar (ystd::vector<>deberíamos haber proporcionado) una especializaciónstd::swap, ¿verdad?Bueno, eso ciertamente funcionaría en este caso, pero tiene un problema evidente: las especializaciones de funciones no pueden ser parciales. Es decir, no podemos especializar clases de plantillas con esto, solo instancias particulares:
Este método funciona algunas veces, pero no todo el tiempo. Debe haber una mejor manera.
¡Ahi esta! Podemos usar una
friendfunción y encontrarla a través de ADL :Cuando queremos intercambiar algo, asociamos †
std::swapy luego hacemos una llamada no calificada:¿Qué es una
friendfunción? Hay confusión en esta área.Antes de que C ++ se estandarizara, las
friendfunciones hicieron algo llamado "inyección de nombre de amigo", donde el código se comportó como si la función se hubiera escrito en el espacio de nombres circundante. Por ejemplo, estos fueron pre-estándar equivalentes:Sin embargo, cuando se inventó ADL, esto se eliminó. La
friendfunción solo se puede encontrar a través de ADL; si lo deseaba como una función libre, debía declararse así ( consulte esto , por ejemplo). Pero he aquí! Había un problema.Si solo usa
std::swap(x, y), su sobrecarga nunca será encontrada, porque ha dicho explícitamente "¡mire adentrostd, y en ningún otro lugar"! Es por eso que algunas personas sugirieron escribir dos funciones: una como una función que se encuentra a través de ADL , y la otra para manejarstd::calificaciones explícitas .Pero como vimos, esto no puede funcionar en todos los casos, y terminamos con un desastre feo. En cambio, el intercambio idiomático fue por la otra ruta: en lugar de hacer que el trabajo de las clases sea el de proporcionar
std::swap, es el trabajo de los intercambiadores asegurarse de que no usen calificadosswap, como se indicó anteriormente. Y esto tiende a funcionar bastante bien, siempre y cuando la gente lo sepa. Pero ahí está el problema: ¡no es intuitivo necesitar usar una llamada no calificada!Para facilitar esto, algunas bibliotecas como Boost proporcionaron la función
boost::swap, que solo realiza una llamada no calificadaswap,std::swapcomo un espacio de nombres asociado. Esto ayuda a que las cosas vuelvan a ser concisas, pero sigue siendo un fastidio.Tenga en cuenta que no hay ningún cambio en el comportamiento de C ++ 11
std::swap, lo que yo y otros pensamos erróneamente que sería el caso. Si te mordió esto, lee aquí .En resumen: la función miembro es solo ruido, la especialización es fea e incompleta, pero la
friendfunción está completa y funciona. Y cuando intercambie, useboost::swapo no calificadoswapconstd::swapasociado.† Informalmente, se asocia un nombre si se considerará durante una llamada de función. Para los detalles, lea §3.4.2. En este caso,
std::swapnormalmente no se considera; pero podemos asociarlo (agregarlo al conjunto de sobrecargas consideradas por no calificadoswap), permitiendo que se encuentre.fuente
std::vector<std::string>().swap(someVecWithData);, lo que no es posible con unaswapfunción libre porque ambos argumentos se pasan por referencia no constante.operator=,operator+yoperator+=, pero claramente se acepta / espera que existan aquellos operadores en clases relevantes por simetría. Lo mismo se aplica a memberswap+ namespace-scopedswapen mi opinión.function<void(A*)> f; if(!f) { }puede fallar solo porqueAdeclara unoperator!que aceptafigual de bien quefel suyooperator!(poco probable, pero puede suceder). Sifunction<>el autor pensara "ohh tengo un 'operador bool', ¿por qué debería implementar 'operador!' ¡Eso violaría DRY!", Eso sería fatal. Solo necesita teneroperator!implementadoAyAtener un constructor para afunction<...>, y las cosas se romperán, porque ambos candidatos requerirán conversiones definidas por el usuario.Ese código es equivalente (en casi todos los sentidos) a:
Una función amiga definida dentro de una clase es:
inlineLas reglas exactas están en la sección
[class.friend](cito los párrafos 6 y 7 del borrador de C ++ 0x):fuente
swapsolo sea visible para ADL. Es un miembro del espacio de nombres adjunto, pero su nombre no es visible para los otros formularios de búsqueda de nombres. EDITAR: veo que @GMan fue más rápido otra vez :) @Ben siempre ha sido así en ISO C ++ :)friendADL solo encuentra funciones, y si solo necesitan ser funciones libres confriendacceso, deben declararse comofrienddentro de la clase y como una declaración de función libre normal fuera de la clase. Puede ver esa necesidad en esta respuesta , por ejemplo.