c++ - operator - ¿Este uso del operador “,” se considera de mala forma?
overload operator c++ (8)
Tengo curiosidad acerca de si debo evitarlo, porque sobre todo el código debe ser comprendido fácilmente por otros programadores
Si el objetivo es hacer que su código sea fácil de entender para otros programadores de C ++, reemplazar a los operadores para darles un significado muy diferente al de C ++ estándar no es un buen comienzo. Los lectores no deberían tener que a) entender cómo has implementado tu contenedor yb) recalibrar su comprensión de los operadores estándar solo para que puedas entender tu código.
Puedo apreciar el precedente de Boost para este tipo de cosas. Si está bastante seguro de que la mayoría de las personas que leerán su código también estarán familiarizadas con Boost Assign, su propia anulación de operador, podría ser bastante razonable. Sin embargo, sugeriría seguir la sugerencia de @ badzeppelin para usar el operador << en su lugar, tal como lo hace iostreams. Se puede contar con que cada desarrollador de C ++ se haya encontrado con un código como:
cout << "Hello world!"`
y su operación de adición es muy similar a escribir en una secuencia.
He creado una clase de lista como medio para reemplazar funciones variadic en mi programa que se usa para inicializar objetos que necesitan contener una lista cambiante de elementos. La clase de lista tiene una sintaxis de uso que realmente me gusta. Sin embargo, no lo he visto usado antes, así que me preguntaba si no debería usarlo solo por ese hecho. Una implementación básica de la clase de lista se ve así ...
#include <list>
#include <iostream>
template<typename T>
struct list
{
std::list<T> items;
list(const list&ref):items(ref.items){}
list(){}
list(T var){items.push_back(var);}
list& operator,(list add_){
items.insert(items.end(),add_.items.begin(), add_.items.end());
return *this;
}
list& operator=(list add_){
items.clear();
items.insert(items.end(),add_.items.begin(), add_.items.end());
return *this;
}
list& operator+=(list add_){
items.insert(items.end(),add_.items.begin(), add_.items.end());
return *this;
}
};
Esto me permite tener esto en código así que ...
struct music{
//...
};
struct music_playlist{
list<music> queue;
//...
};
int main (int argc, const char * argv[])
{
music_playlist playlist;
music song1;
music song2;
music song3;
music song4;
playlist.queue = song1,song2; // The queue now contains song1 and song2
playlist.queue+= song1,song3,song4; //The queue now contains two song1s and song2-4
playlist.queue = song2; //the queue now only contains song2
return 0;
}
Realmente creo que la sintaxis es mucho más agradable de lo que hubiera sido si hubiera expuesto un contenedor de stl regular, e incluso más agradable (y de tipo seguro) que las funciones variadic. Sin embargo, dado que no he visto esta sintaxis utilizada, tengo curiosidad acerca de si debo evitarla, porque sobre todo el código debería ser comprendido fácilmente por otros programadores.
EDITAR:
En conjunto con esta pregunta, he publicado esta question más orientada a las soluciones al problema real.
¿Ha cambiado la precedencia recientemente?
playlist.queue = song1,song2;
Esto debería analizar como
(playlist.queue = song1) , song2;
¡Tu '','' y ''+ ='' son iguales! Sería una mejor coincidencia semántica si su operador de coma creara una lista temporal, insertara los elementos izquierdo y derecho y devolviera el temporal. Entonces podrías escribirlo así;
playlist.queue = (song1,song2);
con paréntesis explícitos. Eso le daría a los programadores C la oportunidad de luchar para poder leer el código.
¿Por qué no sobrecargar el operador <<
como lo hace QList? Entonces úsalo como:
playlist.queue << song1 << song2; // The queue now contains song1 and song2
playlist.queue << song1 << song3 << song4; //The queue now contains two song1s and song2-4
Es malo en muchos niveles ...
Estás anulando list
y shadowing std::list
. Un gran no-no. Si desea su propia clase de lista, haga que sea con un nombre diferente, no oculte la biblioteca estándar.
Usando ,
de esa manera no es legible. El valor de retorno del operador es el operando correcto. Incluso si su código funciona, para un lector externo no será obvio por qué, y es algo malo. El código debe ser legible, no agradable.
Esto es probablemente algo que pertenece a los programadores, pero aquí están mis dos centavos.
Si está hablando de un código que tiene un contexto bastante estrecho, donde los usuarios lo usarán en un par de lugares y eso es todo, entonces la sobrecarga del operador probablemente esté bien. Si está creando un lenguaje específico de dominio que se usa en un dominio particular y en ninguna otra parte, probablemente esté bien.
El problema surge cuando se está sobrecargando por algo que espera que el usuario use con cierta frecuencia.
Sobrecarga ,
significa que el lector debe reinterpretar completamente cómo lee su código. No pueden simplemente mirar una expresión y al instante saber lo que hace. Estás jugando con algunas de las suposiciones más básicas que hacen los programadores de C ++ cuando se trata de escanear código.
Hazlo bajo tu propio riesgo.
Estoy de acuerdo en que tu sintaxis se ve bien como la has escrito.
Mi principal dificultad con el código es que espero que lo siguiente sea el mismo
playlist.queue = song1,song2;
playlist.queue = (song1,song2); //more of c-style, as @Iuser notes.
Mientras que en realidad son completamente diferentes.
Esto es peligroso porque es muy fácil introducir errores de uso en el código. Si a alguien le gusta usar paréntesis para agregar énfasis adicional a las agrupaciones (no infrecuente), la coma podría convertirse en un verdadero dolor. Por ejemplo,
//lets combine differnt playlists
new_playlist.queue = song1 //the first playlist
,(song3,song4) //the second playlist //opps, I didn''t add song 3!
, song5; //the third
o
new_playlist.queue = (old_playlist.queue, song6); //opps, I edited my old playlist too!
Incidentalmente, ¿ha encontrado boost.assign: http://www.boost.org/doc/libs/1_47_0/libs/assign/doc/index.html
No hay nada malo en usar el operator ,
coma operator ,
usando específicamente. Cualquier operador deja mal gusto, si es explotado. En su código, no veo ningún problema razonable. Solo una sugerencia que me gustaría dar es:
list& operator,(list &add_){ // <--- pass by reference to avoid copies
*this += add_; // <--- reuse operator +=
return *this;
}
De esta manera, siempre debe editar solo el operator +=
, si desea algún cambio en la lógica. Tenga en cuenta que mi respuesta está en la perspectiva de la legibilidad y el mantenimiento del código en general . No voy a plantear la preocupación sobre la lógica de negocios que utiliza.
Un pequeño problema es que si el compilador no puede elegir la coma del operador sobrecargado, puede recurrir al uso del operador integrado.
En contraste, con los http://www.boost.org/doc/libs/1_47_0/libs/assign/doc/index.html mezcla http://www.boost.org/doc/libs/1_47_0/libs/assign/doc/index.html produce un error de compilación.
#include <boost/assign.hpp>
int main()
{
int one = 1;
const char* two = "2";
list<int> li;
li = one, two;
using namespace boost::assign;
std::list<int> li2;
li2 += one, two;
}