No entiendo los argumentos contra la sobrecarga del operador [cerrado]

82

Acabo de leer uno de los artículos de Joel en el que dice:

En general, tengo que admitir que tengo un poco de miedo a las características del lenguaje que ocultan cosas . Cuando veas el código

i = j * 5;

... en C sabes, al menos, que j se multiplica por cinco y los resultados se almacenan en i.

Pero si ve el mismo fragmento de código en C ++, no sabe nada. Nada. La única forma de saber lo que realmente está sucediendo en C ++ es averiguar qué tipos son i y j, algo que podría declararse en otro lugar. Esto se debe a que j puede ser de un tipo que se ha operator*sobrecargado y hace algo terriblemente ingenioso cuando intentas multiplicarlo.

(El énfasis es mío). ¿Te asustan las características del lenguaje que ocultan cosas? ¿Cómo puedes tener miedo de eso? ¿No es ocultar cosas (también conocido como abstracción ) una de las ideas clave de la programación orientada a objetos? Cada vez que llama a un método a.foo(b), no tiene idea de lo que podría hacer. Tienes que averiguar qué tipos ay qué bson, algo que podría declararse en otro lugar. Entonces, ¿deberíamos eliminar la programación orientada a objetos, porque oculta demasiadas cosas del programador?

¿Y en qué se j * 5diferencia de j.multiply(5)lo que podría tener que escribir en un idioma que no sea compatible con la sobrecarga del operador? Una vez más, tendrías que averiguar el tipo jy echar un vistazo dentro del multiplymétodo, porque he aquí, jpodría ser de un tipo que tiene un multiplymétodo que hace algo terriblemente ingenioso.

"Muahaha, soy un programador malvado que nombra un método multiply, pero lo que realmente hace es totalmente oscuro y no intuitivo y no tiene absolutamente nada que ver con multiplicar las cosas". ¿Es ese un escenario que debemos tener en cuenta al diseñar un lenguaje de programación? ¡Entonces tenemos que abandonar los identificadores de los lenguajes de programación porque pueden ser engañosos!

Si desea saber qué hace un método, puede echar un vistazo a la documentación o echar un vistazo dentro de la implementación. La sobrecarga del operador es solo azúcar sintáctica, y no veo cómo cambia el juego en absoluto.

Por favor iluminame.

flujo libre
fuente
20
+1: Bien escrito, bien discutido, tema interesante y muy discutible. Un brillante ejemplo de una pregunta p.se.
Allon Guralnek
19
+1: La gente escucha a Joel Spolsky porque escribe bien y es conocido. Pero eso no lo hace correcto el 100% del tiempo. Estoy de acuerdo con tu argumento. Si todos siguiéramos la lógica de Joel aquí, nunca llegaríamos a ninguna parte.
Nadie
55
Yo diría que o i y j se declaran localmente para que pueda ver su tipo rápidamente, o son nombres de variables sucky y deben cambiarse de nombre adecuadamente.
Cameron MacFarland
55
+1, pero no olvides la mejor parte del artículo de Joel: después de correr un maratón hacia la respuesta correcta, sin ninguna razón aparente se detiene a 50 pies de distancia. El código incorrecto no solo debería verse mal; No debería compilar.
Larry Coleman
3
@Larry: Puede hacer que el código incorrecto no se compile definiendo las clases adecuadamente, por lo que en su ejemplo podría tener SafeString y UnsafeString en C ++, o RowIndex y ColumnIndex, pero luego tendría que usar la sobrecarga del operador para que se comporten de manera intuitiva.
David Thornley

Respuestas:

32

La abstracción 'oculta' el código para que no tenga que preocuparse por el funcionamiento interno y, a menudo, para que no pueda cambiarlo, pero la intención no era evitar que lo mirara. Simplemente hacemos suposiciones sobre los operadores y, como dijo Joel, podría estar en cualquier lugar. Tener una función de programación que requiera que todos los operadores sobrecargados se establezcan en una ubicación específica puede ayudar a encontrarlo, pero no estoy seguro de que facilite su uso.

No veo que hacer * haga algo que no se parezca mucho a la multiplicación mejor que una función llamada Get_Some_Data que elimina datos.

JeffO
fuente
13
+1 Para el bit 'No veo'. Las características del lenguaje están ahí para su uso, no abuso.
Michael K
55
Sin embargo, tenemos un <<operador definido en secuencias que no tiene nada que ver con el desplazamiento a nivel de bits, directamente en la biblioteca estándar de C ++.
Malcolm
el operador 'cambio bit a bit' solo se llama así por razones históricas. Cuando se aplica a los tipos estándar, realiza un cambio a nivel de bits (de la misma manera que el operador + agrega números juntos cuando se aplica a los tipos numéricos), sin embargo, cuando se aplica a un tipo complejo, puede hacer lo que quiera, siempre que lo haga sentido para ese tipo.
gbjbaanb
1
* también se usa para desreferenciar (como lo hacen los punteros e iteradores inteligentes); no está claro dónde poner el límite entre la sobrecarga buena y mala
martinkunev
No estaría en cualquier lugar, estaría en la definición de tipo de j.
Andy
19

En mi humilde opinión, las características del lenguaje, como la sobrecarga del operador, le dan al programador más potencia. Y, como todos sabemos, un gran poder conlleva una gran responsabilidad. Las características que le dan más poder también le brindan más formas de dispararse en el pie y, obviamente, deben usarse con prudencia.

Por ejemplo, tiene mucho sentido sobrecargar el +o el *operador para class Matrixo class Complex. Todos sabrán instantáneamente lo que significa. Por otro lado, para mí el hecho de que eso +significa concatenación de cadenas no es del todo obvio, a pesar de que Java hace esto como parte del lenguaje, y STL lo hace por std::stringusar la sobrecarga del operador.

Otro buen ejemplo de cuándo la sobrecarga del operador hace que el código sea más claro son los punteros inteligentes en C ++. Desea que los punteros inteligentes se comporten como punteros regulares tanto como sea posible, por lo que tiene mucho sentido sobrecargar a los operadores *y al unario ->.

En esencia, la sobrecarga del operador no es más que otra forma de nombrar una función. Y hay una regla para nombrar funciones: el nombre debe ser descriptivo, lo que hace que sea inmediatamente obvio lo que hace la función. La misma regla exacta se aplica a la sobrecarga del operador.

Dima
fuente
1
Sus últimas dos oraciones llegan al centro de la objeción a la sobrecarga del operador: el deseo de que todo el código sea inmediatamente obvio.
Larry Coleman
2
¿No es obvio lo que significa M * N, donde M y N son de tipo Matrix?
Dima
2
@ Fred: No. Hay un tipo de multiplicación matricial. Puede multiplicar una matriz mxn por una matriz nxk y obtener una matriz mxk.
Dima
1
@FredOverflow: hay diferentes maneras de multiplicar un vector tridimensional, uno que le da un escalar y otro que le da otro vector tridimensional, por lo que la sobrecarga *de esos puede causar confusión. Podría decirse que podría usarlo operator*()para el producto punto y operator%()para el producto cruzado, pero no lo haría para una biblioteca de uso general.
David Thornley
2
@ Martin Beckett: ++ No. C no está permitido para reordenar A-Bcomo B-Atampoco, y todos los operadores sigue ese patrón. Aunque siempre hay una excepción: cuando el compilador puede demostrar que no importa, se le permite reorganizar todo.
Sjoerd
9

En Haskell "+", "-", "*", "/" etc. son solo funciones (infijo).

¿Debería nombrar una función infija "más" como en "4 más 2"? Por qué no, si además es lo que hace su función. ¿Debería nombrar su función "más" "+"? Por qué no.

Creo que el problema con los llamados "operadores" es que se parecen principalmente a las operaciones matemáticas y no hay muchas maneras de interpretarlas y, por lo tanto, hay altas expectativas sobre lo que hace un método / función / operador.

EDITAR: dejó mi punto más claro

LennyProgrammers
fuente
Erm, excepto por lo que se hereda de C, C ++ (y eso es lo que Fred preguntaba) hace más o menos lo mismo. ¿Cuál es su opinión sobre si esto es bueno o malo?
sbi
@sbi Me encanta la sobrecarga de operadores ... De hecho, incluso los operadores de C ha sobrecargados ... Se puede utilizar para int, float, long longy lo que sea. Entonces, ¿de qué se trata todo eso?
FUZxxl
@FUZxxl: Esto se trata de operadores definidos por el usuario que sobrecargan los integrados.
sbi
1
@sbi Haskell no tiene distinción entre incorporado y definido por el usuario . Todos los operadores son iguales. Incluso puede activar algunas extensiones que eliminen todas las cosas predefinidas y le permitan escribir cualquier cosa desde cero, incluidos los operadores.
FUZxxl
@FUZxxl: Eso bien podría ser, pero los operadores opuestos sobrecargados generalmente no se oponen al uso integrado +para diferentes tipos de números incorporados, sino a la creación de sobrecargas definidas por el usuario. De ahí mi comentario.
sbi
7

Basado en las otras respuestas que he visto, solo puedo concluir que la verdadera objeción a la sobrecarga del operador es el deseo de un código obvio de inmediato.

Esto es trágico por dos razones:

  1. Llevado a su conclusión lógica, el principio de que el código debería ser inmediatamente obvio nos obligaría a todos a seguir codificando en COBOL.
  2. No aprende del código que es inmediatamente obvio. Aprende del código que tiene sentido una vez que se toma un tiempo para pensar cómo funciona.
Larry Coleman
fuente
Sin embargo, aprender del código no siempre es el objetivo principal. En un caso como "la función X falla, la persona que la escribió dejó la empresa y usted necesita arreglarla lo antes posible", preferiría tener un código que sea obvio de inmediato.
Erroresatz
5

Estoy un poco de acuerdo.

Si escribe multiply(j,5), jpodría ser de tipo escalar o matricial, haciendo multiply()más o menos complejo, dependiendo de lo que jsea. Sin embargo, si abandona la idea de sobrecargar por completo, entonces la función tendría que nombrarse multiply_scalar()o multiply_matrix()lo que haría obvio lo que está sucediendo debajo.

Hay un código donde muchos de nosotros lo preferiríamos de una manera y hay un código donde la mayoría de nosotros lo preferiría de la otra manera. La mayor parte del código, sin embargo, cae en el punto medio entre esos dos extremos. Lo que prefiera allí depende de sus antecedentes y preferencias personales.

sbi
fuente
Buen punto. Sin embargo, abandonar la sobrecarga por completo no funciona bien con la programación genérica ... ''
Fredoverflow
@FredO: Por supuesto que no. Pero la programación genérica se trata de usar el mismo algoritmo para tipos muy diferentes, por lo que a quienes prefieran multiply_matrix()no les gustará tampoco la programación genérica.
sbi
2
Eres bastante optimista sobre los nombres, ¿verdad? Según algunos lugares en los que he trabajado, esperaría nombres como 'multiply ()' y 'multiplym ()' o tal vez más real_multiply()o menos. Los desarrolladores a menudo no son buenos con los nombres, y operator*()al menos va a ser consistente.
David Thornley
@David: Sí, omití el hecho de que los nombres podrían ser malos. Pero entonces también podríamos suponer que operator*()podría hacer algo estúpido, jes una macro que evalúa expresiones que involucran cinco llamadas a funciones, y otras cosas. Entonces ya no puedes comparar los dos enfoques. Pero sí, nombrar bien las cosas es difícil, aunque vale la pena el tiempo que sea necesario.
sbi
55
@David: Y dado que nombrar cosas es difícil, los nombres deberían ser desterrados de los lenguajes de programación, ¿verdad? ¡Es demasiado fácil equivocarse! ;-)
fredoverflow
4

Veo dos problemas con la sobrecarga del operador.

  1. La sobrecarga cambia la semántica del operador, incluso si el programador no tiene esa intención. Por ejemplo, cuando se sobrecarga &&, ||o ,, se pierden los puntos de secuencia que están implícitos en las variantes incorporadas de estos operadores (así como el comportamiento de los cortocircuitos de los operadores lógicos). Por esta razón, es mejor no sobrecargar estos operadores, incluso si el idioma lo permite.
  2. Algunas personas ven la sobrecarga del operador como una característica agradable, comienzan a usarla en todas partes, incluso si no es la solución adecuada. Esto hace que otras personas reaccionen de forma exagerada en la otra dirección y advierten contra el uso de sobrecarga del operador. No estoy de acuerdo con ninguno de los grupos, pero tomo el término medio: la sobrecarga del operador debe usarse con moderación y solo cuando
    • El operador sobrecargado tiene el significado natural tanto para los expertos en dominios como para los expertos en software. Si esos dos grupos no están de acuerdo con el significado natural para el operador, no lo sobrecargue.
    • Para los tipos involucrados, no existe un significado natural para el operador y el contexto inmediato (preferiblemente la misma expresión, pero no más que unas pocas líneas) siempre deja en claro cuál es el significado del operador. Un ejemplo de esta categoría sería operator<<para las transmisiones.
Bart van Ingen Schenau
fuente
1
+1 de mí, pero el segundo argumento puede aplicarse igualmente a la herencia. Muchas personas no tienen idea de la herencia y tratan de aplicarla a todo. Creo que la mayoría de los programadores estarían de acuerdo en que es posible hacer un mal uso de la herencia. ¿Eso significa que la herencia es "malvada" y debería ser abandonada de los lenguajes de programación? ¿O deberíamos dejarlo puesto porque también puede ser útil?
fredoverflow
@FredOverflow El segundo argumento se puede aplicar a cualquier cosa que sea "nueva y actual". No lo estoy dando como argumento para eliminar la sobrecarga de operadores de un lenguaje, sino como una razón por la cual las personas argumentan en contra. En lo que a mí respecta, la sobrecarga del operador es útil, pero debe usarse con cuidado.
Bart van Ingen Schenau
En mi humilde opinión, permitir sobrecargas &&y ||de una manera que no implique secuenciación fue un gran error (en mi humilde opinión, si C ++ iba a permitir la sobrecarga de esos, debería haber utilizado un formato especial de "dos funciones", con la primera función requerida para devolver un tipo que era convertible implícitamente a un número entero; la segunda función podría tomar dos o tres argumentos, siendo el argumento "extra" de la segunda función el tipo de retorno de la primera. El compilador llamará a la primera función y luego, si devuelve un valor distinto de cero, evalúe el segundo operando y llame a la segunda función).
supercat
Por supuesto, eso no es tan extraño como permitir que el operador de coma se sobrecargue. Por cierto, una cosa sobrecargada que no he visto realmente, pero me gustaría, sería un medio de acceso estricto a los miembros, permitiendo que una clase como foo.bar[3].Xsea ​​manejada por foola clase, en lugar de requerir fooexponer un miembro que podría admitir la suscripción y luego exponer a un miembro X. Si uno quisiera forzar la evaluación a través del acceso real de un miembro, uno escribiría ((foo.bar)[3]).X.
supercat
3

Según mi experiencia personal, la forma en Java de permitir múltiples métodos, pero no sobrecargar al operador, significa que cada vez que ve a un operador sabe exactamente lo que hace.

No tiene que ver si *invoca un código extraño, pero sepa que es una multiplicación y que se comporta exactamente como se define en la Especificación del lenguaje Java. Esto significa que puede concentrarse en el comportamiento real en lugar de descubrir todas las cosas de wicket definidas por el programador.

En otras palabras, prohibir la sobrecarga del operador es un beneficio para el lector , no para el escritor , y por lo tanto, hace que los programas sean más fáciles de mantener.


fuente
+1, con una advertencia: C ++ te da suficiente cuerda para ahorcarte. Pero si quiero implementar una lista vinculada en C ++, me gustaría poder usar [] para acceder al enésimo elemento. Tiene sentido usar los operadores para datos para los que (matemáticamente hablando) son válidos.
Michael K
@Michael, ¿no puedes vivir con la list.get(n)sintaxis?
@ Thorbjørn: En realidad también está bien, quizás un mal ejemplo. El tiempo puede ser mejor - sobrecargar +, - tendría sentido en lugar de time.add (anotherTime).
Michael K
44
@Michael: Acerca de las listas vinculadas, std::listno se sobrecarga operator[](ni proporciona ningún otro medio de indexación en la lista), porque dicha operación sería O (n), y una interfaz de lista no debería exponer dicha función si le importa la eficiencia. Los clientes pueden verse tentados a iterar sobre listas vinculadas con índices, haciendo innecesariamente los algoritmos O (n) O (n ^ 2). Esto se ve con bastante frecuencia en el código Java, especialmente si las personas trabajan con la Listinterfaz que tiene como objetivo eliminar la complejidad por completo.
fredoverflow
55
@Thor: "Pero para estar seguro, debe verificar :)" ... De nuevo, esto no está relacionado con la sobrecarga del operador . Si ve time.add(anotherTime), también tendrá que verificar si el programador de la biblioteca implementó la operación de agregar "correctamente" (lo que sea que eso signifique).
fredoverflow
3

Una diferencia entre sobrecargar a * by llamar multiply(a,b)es que esta última puede ser fácilmente buscada. Si la multiplyfunción no está sobrecargada para diferentes tipos, puede averiguar exactamente qué va a hacer la función, sin tener que rastrear los tipos de ay b.

Linus Torvalds tiene un argumento interesante sobre la sobrecarga del operador. En algo como el desarrollo del kernel de Linux, donde la mayoría de los cambios se envían a través de parches por correo electrónico, es importante que los encargados del mantenimiento puedan comprender qué hará un parche con solo unas pocas líneas de contexto alrededor de cada cambio. Si las funciones y los operadores no están sobrecargados, entonces el parche se puede leer más fácilmente de forma independiente del contexto, ya que no tiene que revisar el archivo modificado para determinar cuáles son todos los tipos y verificar si hay operadores sobrecargados.

Scott Wales
fuente
¿No está el kernel de Linux desarrollado en C puro? ¿Por qué hablar sobre la sobrecarga (del operador) en este contexto?
fredoverflow
Las preocupaciones son las mismas para cualquier proyecto con un proceso de desarrollo similar, independientemente del idioma. La sobrecarga excesiva puede dificultar la comprensión del impacto de los cambios si solo tiene que seguir unas pocas líneas de un archivo de revisión.
Scott Wales
@FredOverflow: El kernel de Linux está en GCC C realmente. Utiliza todo tipo de extensiones que le dan a su C una sensación casi C ++ en algunos momentos. Estoy pensando en algunas de las manipulaciones de tipo elegante.
Zan Lynx
2
@Scott: No tiene sentido discutir la "maldad" de la sobrecarga con respecto a los proyectos programados en C, porque C no tiene la capacidad de sobrecargar las funciones.
fredoverflow
3
Linus Torvalds me parece que tiene un punto de vista estrecho. A veces critica cosas que no son realmente útiles para la programación del kernel de Linux como si eso las hiciera inadecuadas para uso general. Subversion es un ejemplo. Es un buen VCS, pero el desarrollo del kernel de Linux realmente necesita un VCS distribuido, por lo que Linus criticó a SVN en general.
David Thornley
2

Sospecho que tiene algo que ver con romper las expectativas. Estoy acostumbrado a C ++, estás acostumbrado a que el comportamiento del operador no esté completamente dictado por el lenguaje, y no te sorprenderás cuando un operador hace algo extraño. Si está acostumbrado a los idiomas que no tienen esa función, y luego ve el código C ++, trae consigo las expectativas de esos otros idiomas, y puede sorprenderse cuando descubre que un operador sobrecargado hace algo raro.

Personalmente creo que hay una diferencia. Cuando puede cambiar el comportamiento de la sintaxis incorporada del lenguaje, se vuelve más opaco para razonar. Los lenguajes que no permiten la metaprogramación son sintácticamente menos potentes, pero conceptualmente más fáciles de entender.

Joeri Sebrechts
fuente
Los operadores sobrecargados nunca deberían hacer "algo extraño". Está bien si hace algo complejo, por supuesto. Pero solo se sobrecarga cuando tiene un significado obvio.
Sjoerd
2

Creo que la sobrecarga de operadores matemáticos no es el problema real con la sobrecarga de operadores en C ++. Creo que sobrecargar operadores que no deberían depender del contexto de la expresión (es decir, tipo) es "malvado". Por ejemplo, sobrecarga , [ ] ( ) -> ->* new deleteo incluso el unario *. Tienes un cierto conjunto de expectativas de esos operadores que nunca deberían cambiar.

Allon Guralnek
fuente
+1 No haga que [] sea el equivalente de ++.
Michael K
3
¿Estás diciendo que no debemos ser capaces de sobrecargar los operadores que usted ha mencionado en absoluto ? ¿O simplemente está diciendo que deberíamos sobrecargarlos solo con fines sanos? Porque odiaría ver contenedores sin operator[], functors sin operator(), punteros inteligentes sin operator->y así sucesivamente.
fredoverflow
Estoy diciendo que el problema potencial de sobrecarga del operador con operaciones matemáticas es pequeño en comparación con esos operadores. Hacer algo inteligente o loco con los operadores matemáticos puede ser problemático, pero los operadores que enumeré, que las personas generalmente no consideran operadores sino elementos básicos del lenguaje, siempre deben cumplir con las expectativas definidas por el lenguaje. []siempre debe ser un descriptor de acceso tipo matriz y ->siempre debe significar acceder a un miembro. No importa si es realmente una matriz o un contenedor diferente, o si es un puntero inteligente o no.
Allon Guralnek
2

Entiendo perfectamente que no te gusta el argumento de Joel sobre esconderse. Yo tampoco. De hecho, es mucho mejor usar '+' para cosas como los tipos numéricos incorporados o para los propios, como, por ejemplo, matriz. Admito que esto es ordenado y elegante para poder multiplicar dos matrices con '*' en lugar de '.multiply ()'. Y después de todo, tenemos el mismo tipo de abstracción en ambos casos.

Lo que duele aquí es la legibilidad de su código. En casos de la vida real, no en el ejemplo académico de la multiplicación de matrices. Especialmente si su idioma permite definir operadores que no están inicialmente presentes en el núcleo del idioma, por ejemplo =:=. En este punto surgen muchas preguntas adicionales. ¿De qué se trata ese maldito operador? Quiero decir, ¿cuál es la precedencia de esa cosa? ¿Qué es la asociatividad? ¿En qué orden se a =:= b =:= cejecuta realmente?

Eso ya es un argumento contra la sobrecarga del operador. ¿Todavía no está convencido? ¿Comprobar las reglas de precedencia no te llevó más de 10 segundos? Ok, vamos más allá.

Si comienza a usar un lenguaje que permite la sobrecarga de operadores, por ejemplo, el popular cuyo nombre comienza con 'S', aprenderá rápidamente que a los diseñadores de bibliotecas les encanta anular operadores. Por supuesto, están bien educados, siguen las mejores prácticas (sin cinismo aquí) y todas sus API tienen mucho sentido cuando las miramos por separado.

Ahora imagine que tiene que usar algunas API que hacen un uso intensivo de los operadores que se sobrecargan en una sola pieza de código. O incluso mejor: tienes que leer un código heredado como ese. Esto es cuando la sobrecarga del operador realmente apesta. Básicamente, si hay muchos operadores sobrecargados en un lugar, pronto comenzarán a mezclarse con los otros caracteres no alfanuméricos en el código de su programa. Se mezclarán con caracteres no alfanuméricos que no son realmente operadores, sino algunos elementos de gramática del lenguaje más fundamentales que definen cosas como bloques y ámbitos, declaraciones de control de flujo de forma o denotan algunas cosas meta. Tendrá que ponerse las gafas y acercar los ojos 10 cm a la pantalla LCD para comprender ese desorden visual.

akosicki
fuente
1
Sobrecargar a los operadores existentes e inventar nuevos operadores no es lo mismo, pero yo hago +1.
fredoverflow
1

En general, evito usar la sobrecarga del operador de manera no intuitiva. Es decir, si tengo una clase numérica, la sobrecarga * es aceptable (y se recomienda). Sin embargo, si tengo un empleado de clase, ¿qué haría la sobrecarga *? En otras palabras, sobrecargue a los operadores de formas intuitivas que faciliten su lectura y comprensión.

Aceptable / Animado:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Inaceptable:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};
Zac Howland
fuente
1
¿Multiplicando empleados? Seguramente es una ofensa descartable, si lo hacen en la mesa de la sala de juntas, eso es.
gbjbaanb
1

Además de lo que ya se ha dicho aquí, hay un argumento más en contra de la sobrecarga del operador. De hecho, si escribe +, es obvio que quiere decir la adición de algo a algo. Pero este no es siempre el caso.

C ++ en sí mismo proporciona un gran ejemplo de tal caso. ¿Cómo se stream << 1supone que debe leerse? corriente desplazada a la izquierda por 1? No es obvio en absoluto a menos que sepa explícitamente que << en C ++ también escribe en la secuencia. Sin embargo, si esta operación se implementara como un método, ningún desarrollador sensato escribiría o.leftShift(1), sería algo así o.write(1).

La conclusión es que al hacer que la sobrecarga del operador no esté disponible, el lenguaje hace que los programadores piensen en los nombres de las operaciones. Incluso si el nombre elegido no es perfecto, aún es más difícil interpretar mal un nombre que un signo.

Malcolm
fuente
1

En comparación con los métodos detallados, los operadores son más cortos, pero tampoco requieren paréntesis. Los paréntesis son relativamente inconvenientes para escribir. Y debes equilibrarlos. En total, cualquier llamada al método requiere tres caracteres de ruido simple en comparación con un operador. Esto hace que el uso de operadores sea muy, muy tentador.
¿Por qué si alguien querría esto cout << "Hello world"?

El problema con la sobrecarga es que la mayoría de los programadores son increíblemente flojos y la mayoría de los programadores no pueden permitirse el lujo de serlo.

Lo que lleva a los programadores de C ++ al abuso de la sobrecarga del operador no es su presencia, sino la ausencia de una forma más ordenada de realizar llamadas a métodos. Y las personas no solo tienen miedo de la sobrecarga del operador porque es posible, sino porque ya está hecho.
Tenga en cuenta que, por ejemplo, en Ruby y Scala, nadie tiene miedo de la sobrecarga del operador. Además del hecho de que el uso de operadores no es realmente más corto que los métodos, otra razón es que Ruby limita la sobrecarga de operadores a un mínimo razonable, mientras que Scala le permite declarar sus propios operadores, lo que hace que evitar colisiones sea trivial.

back2dos
fuente
o, en C #, por usar + = para vincular un evento a un delegado. No creo que culpar a las características del lenguaje por la estupidez del programador es una forma constructiva de avanzar.
gbjbaanb
0

La razón por la que la sobrecarga de operadores es aterradora es porque hay una gran cantidad de programadores que ni siquiera PENSARÍAN que *no significa simplemente "multiplicar", mientras que un método como foo.multiply(bar)al menos instantáneamente señala a ese programador que alguien escribió un método de multiplicación personalizado . En ese momento se preguntarían por qué e investigarían.

He trabajado con "buenos programadores" que estaban en posiciones de alto nivel que crearían métodos llamados "CompareValues" que tomarían 2 argumentos, y aplicarían los valores de uno a otro y devolverían un valor booleano. O un método llamado "LoadTheValues" que iría a la base de datos para otros 3 objetos, obtendría valores, haría cálculos, lo modificaría thisy lo guardaría en la base de datos.

Si estoy trabajando en un equipo con ese tipo de programadores, instantáneamente sé investigar en qué han trabajado. Si sobrecargan a un operador, no tengo forma de saber que lo hicieron, excepto asumir que lo hicieron e ir a buscar.

En un mundo perfecto, o en un equipo con programadores perfectos, la sobrecarga del operador es probablemente una herramienta fantástica. Sin embargo, todavía tengo que trabajar en un equipo de programadores perfectos, por eso da miedo.

James P. Wright
fuente