telefonica por poner mes linea internet españa cuesta cuanto casa barato c++ class operator-overloading member-functions

c++ - por - o2 españa



¿Por qué algunos operadores solo pueden estar sobrecargados como funciones de miembro, otros como funciones de amigo y el resto como ambos? (4)

¿Por qué algunos operadores solo pueden sobrecargarse como funciones miembro, otros como funciones "libres" no miembros y el resto como ambos?

¿Cuál es el razonamiento detrás de esos?

¿Cómo recordar qué operadores se pueden sobrecargar como qué (miembro, gratuito o ambos)?


Aquí hay un ejemplo: cuando está sobrecargando el << operator para una class T la firma será:

std::ostream operator<<(std::ostream& os, T& objT )

donde la implementación necesita ser

{ //write objT to the os return os; }

Para el operador << , el primer argumento debe ser el objeto ostream y el segundo argumento su objeto de clase T.

Si intenta definir operator<< como una función miembro, no podrá definirlo como std::ostream operator<<(std::ostream& os, T& objT) . Esto se debe a que las funciones miembro del operador binario solo pueden tomar un argumento y el objeto invocado se pasa implícitamente como el primer argumento que usa this .

Si usa la std::ostream operator<<(std::ostream& os) como una función miembro, en realidad terminará con una función miembro std::ostream operator<<(this, std::ostream& os) que lo hará no hagas lo que quieras Por lo tanto, necesita un operador que no sea una función miembro y pueda acceder a los datos de los miembros (si su clase T tiene datos privados que desea transmitir, el operator<< necesita ser un amigo de la clase T).


La pregunta enumera tres clases de operadores. Reunirlos en una lista ayuda, creo, a entender por qué algunos operadores tienen restricciones sobre dónde pueden estar sobrecargados:

  1. Operadores que deben estar sobrecargados como miembros. Estos son bastante pocos:

    1. El operator=() asignación operator=() . Permitir asignaciones que no sean miembros parece abrir la puerta para que los operadores asalten las asignaciones, por ejemplo, al sobrecargar las diferentes versiones de las calificaciones de const . Dado que los operadores de asignación son bastante fundamentales, eso parece ser indeseable.
    2. La función llamada operator()() . Las reglas de llamada de función y sobrecarga son lo suficientemente complicadas como están. Parece desacertado complicar aún más las reglas permitiendo operadores de llamadas a funciones no miembros.
    3. El operator[]() subíndice operator[]() . Usando tipos de índices interesantes parece que podría interferir con los accesos a los operadores. A pesar de que existe poco peligro de secuestro de sobrecargas, no parece haber mucha ganancia sino un potencial interesante para escribir código altamente no obvio.
    4. El operator->() acceso miembro de clase operator->() . Off-hand I no puedo ver ningún mal abuso de sobrecargar este operador un no miembro. Por otro lado, tampoco puedo ver ninguno. Además, el operador de acceso de miembro de clase tiene reglas bastante especiales y jugar con sobrecargas potenciales que interfieren con estas parece una complicación innecesaria.

    Aunque es posible sobrecargar cada uno de estos miembros que no son miembros (especialmente el operador de subíndices que funciona en arreglos / punteros y estos pueden estar a cada lado de la llamada) parece sorprendente si, por ejemplo, una asignación podría ser secuestrada por una sobrecarga no miembro, que es una mejor coincidencia que una de las asignaciones de miembros. Estos operadores también son bastante asimétricos: en general, no desearía admitir la conversión en ambos lados de una expresión que implique a estos operadores.

    Dicho esto, por ejemplo, para una biblioteca de expresiones lambda sería bueno si fuera posible sobrecargar a todos estos operadores y no creo que haya una razón técnica inherente para evitar que estos operadores se puedan descargar.

  2. Operadores que deben estar sobrecargados como funciones no miembro.

    1. El operator"" name() literal definido por el usuario operator"" name()

    Este operador es algo así como una pelota extraña y, posiblemente, realmente no sea realmente un operador. En cualquier caso, no hay ningún objeto para llamar a este miembro para el cual se podrían definir los miembros: el argumento de la izquierda de los literales definidos por el usuario siempre son tipos incorporados.

  3. No se menciona en la pregunta, pero también hay operadores que no se pueden sobrecargar en absoluto:

    1. El selector de miembros .
    2. El operador de acceso a objetos de puntero a miembro .*
    3. El operador del alcance ::
    4. ¿El operador ternario ?:

    Se consideró que estos cuatro operadores eran demasiado fundamentales para interferir en absoluto. Aunque hubo una propuesta para permitir la sobrecarga del operator.() En algún momento no hay una gran ayuda para hacerlo (el caso de uso principal sería referencias inteligentes). Aunque ciertamente hay algunos contextos imaginables en los que sería bueno sobrecargar estos operadores también.

  4. Operadores que pueden estar sobrecargados como miembros o como no miembros. Este es el grueso de los operadores:

    1. El operator++() predeceso y posterior incremento / incremento operator++() , operator--() , operator++(int) , operator--(int)
    2. El operador de desreferencia [unario] operator*()
    3. El operador de dirección [unario] operator&()
    4. El [unario] firma operator+() , operator-()
    5. El operator!() negación lógica operator!() (O el operator not() )
    6. El operator~() inversión bit a bit operator~() (o operator compl() )
    7. El operator==() comparaciones operator==() , operator!=() , operator<() , operator>() , operator<=() y operator>()
    8. El operator+() aritmético [binario] operator+() , operator-() , operator*() , operator/() , operator%()
    9. El operator&() bit a bit [binario] operator&() (o operator bitand() ), operator|() (o operator bit_or() ), operator^() (o operator xor() )
    10. El operator<<() desplazamiento bit a bit operator<<() y el operator>>()
    11. El operator||() lógico operator||() (o el operator or() ) y el operator&&() (u operator and() )
    12. El operator@=() operación / asignación operator@=() (para @ es un símbolo de operador adecuado ()
    13. El operator,() secuencia operator,() (¡cuya sobrecarga en realidad mata a la propiedad de secuencia!)
    14. El puntero al operator->*() acceso de puntero a miembro operator->*()
    15. El operator new() gestión de memoria operator new() , operator new[]() , operator new[]() y operator delete[]()

    Los operadores que pueden estar sobrecargados como miembros o como no miembros no son tan necesarios para el mantenimiento de objetos fundamentales como los otros operadores. Eso no quiere decir que no sean importantes. De hecho, esta lista contiene algunos operadores donde es bastante cuestionable si deberían poderse cargar (por ejemplo, el operator&() de dirección operator&() o los operadores que normalmente causan la secuencia, es decir, operator,() , operator||() , y operator&&() .

Por supuesto, el estándar de C ++ no brinda un fundamento sobre por qué las cosas se hacen de la manera en que se hacen (y tampoco hay registros de los primeros días cuando se tomaron estas decisiones). El mejor razonamiento se puede encontrar probablemente en "Diseño y Evolución de C ++" por Bjarne Stroustrup. Recuerdo que los operadores se discutieron allí, pero no parece haber una versión electrónica disponible.

En general, no creo que haya razones realmente sólidas para las restricciones, aparte de las posibles complicaciones, que en su mayoría no se consideraron que valieran la pena. Sin embargo, dudaría que las restricciones se eliminen ya que las interacciones con el software existente están destinadas a cambiar el significado de algún programa de maneras impredecibles.


La razón fundamental es que no tendría sentido que no sean miembros, ya que lo que está en el lado izquierdo del operador debe ser una instancia de clase.

Por ejemplo, suponiendo una clase A

A a1; .. a1 = 42;

La última declaración es realmente una llamada como esta:

a1.operator=(42);

No tendría sentido para la cosa en el LHS del . no ser una instancia de A, por lo que la función debe ser un miembro.


Porque no puedes modificar la semántica de los tipos primitivos. No tendría sentido definir cómo funciona operator= en un int , cómo deferencia un puntero o cómo funciona el acceso a un arreglo.