Cuándo sobrecargar el operador de coma?

Veo preguntas sobre SO de vez en cuando sobre la sobrecarga del operador de coma en C ++ (principalmente sin relación con la sobrecarga en sí, pero cosas como la noción de puntos de secuencia), y me hace pensar:

¿Cuándo debes sobrecargar la coma? ¿Cuáles son algunos ejemplos de sus usos prácticos?

No puedo pensar en ningún ejemplo en la parte superior de mi cabeza donde he visto o necesitado algo como

foo, bar; 

en el código del mundo real, por lo que tengo curiosidad sobre cuándo (si alguna vez) se usa realmente.

Cambiemos un poco el énfasis a:

¿Cuándo debes sobrecargar la coma?

La respuesta: nunca.

La excepción: si está haciendo metaprogtwigción de plantillas, el operator, tiene un lugar especial en la parte inferior de la lista de prioridades del operador, que puede ser útil para construir protecciones SFINAE, etc.

Los únicos dos usos prácticos que he visto del operator, de sobrecarga son ambos en Boost :

  • Boost.Assign
  • Boost.Phoenix : es fundamental aquí ya que permite que Phoenix lambdas sea compatible con múltiples declaraciones

He usado el operador de coma para indexar mapas con múltiples índices.

 enum Place {new_york, washington, ...}; pair operator , (Place p1, Place p2) { return make_pair(p1, p2); } map< pair, double> distance; distance[new_york, washington] = 100; 

Boost.Assign lo usa para que pueda hacer cosas como:

 vector v; v += 1,2,3,4,5,6,7,8,9; 

Y lo he visto usado para hacks de lenguaje extravagantes, voy a ver si puedo encontrar alguno.


Ajá, recuerdo uno de esos usos estrafalarios: coleccionar expresiones múltiples . (Advertencia, magia oscura.)

La coma tiene una propiedad interesante ya que puede tomar un parámetro de tipo void . Si es el caso, entonces se usa el operador de coma incorporado.

Esto es útil cuando quieres determinar si una expresión tiene un tipo vacío:

 namespace detail_ { template  struct tag { static T get(); }; template  tag operator,(T, tag); template  tag operator,(tag, tag); } #define HAS_VOID_TYPE(expr) \ (sizeof((::detail_::tag(), \ (expr), \ ::detail_::tag).get()) == 1) 

Dejo que el lector descubra como ejercicio lo que está sucediendo. Recuerde que el operator, asocia a la derecha.

De forma similar al ejemplo de Boost.Assign de @GMan , Blitz ++ sobrecarga el operador de coma para proporcionar una syntax conveniente para trabajar con matrices multidimensionales. Por ejemplo:

 Array y(4,4); // A 4x4 array of double y = 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1; 

En SOCI – La biblioteca de acceso a la base de datos de C ++ se utiliza para la implementación de la parte entrante de la interfaz:

 sql << "select name, salary from persons where id = " << id, into(name), into(salary); 

De la base de preguntas frecuentes :

P: El operador de coma sobrecargado es solo ofuscación, no me gusta.

Bien, considere lo siguiente:

"Envíe la consulta X al servidor Y y coloque el resultado en la variable Z."

Arriba, el "y" desempeña un papel de la coma. Incluso si la sobrecarga del operador de coma no es una práctica muy popular en C ++, algunas bibliotecas lo hacen, logrando una syntax breve y fácil de aprender. Estamos bastante seguros de que en SOCI el operador de coma se sobrecargó con un buen efecto.

Una posibilidad es la biblioteca Boost Assign (aunque estoy bastante seguro de que algunas personas considerarían este abuso en lugar de un buen uso).

Boost Spirit probablemente también sobrecarga al operador de coma (sobrecarga casi todo lo demás …)

En la misma línea, me enviaron una solicitud de extracción github con sobrecarga de operador de coma. Parecía algo así como seguir

 class Mylogger { public: template  Mylogger & operator,(const T & val) { std::cout << val; return * this; } }; #define Log(level,args...) \ do { Mylogger logv; logv,level, ":", ##args; } while (0) 

entonces en mi código puedo hacer:

  Log(2, "INFO: setting variable \", 1, "\"\n"); 

¿Alguien puede explicar por qué este es un caso de uso bueno o malo?

Uno de los usos prácticos es para utilizarlo con argumentos variables en macro. Por cierto, los argumentos variables anteriormente eran una extensión en GCC y ahora una parte del estándar C ++ 11.

Supongamos que tenemos una class X , que agrega un objeto de tipo A en ella. es decir

 class X { public: X& operator+= (const A&); }; 

¿Qué ocurre si queremos agregar 1 o más objetos de A en el X buffer; ?
Por ejemplo,

 #define ADD(buffer, ...) buffer += __VA_ARGS__ 

Por encima de la macro, si se usa como:

 ADD(buffer, objA1, objA2, objA3); 

entonces se expandirá a:

 buffer += objA1, objeA2, objA3; 

Por lo tanto, este será un ejemplo perfecto de usar el operador de coma, ya que los argumentos de la variable se expanden con el mismo.

Entonces, para resolver esto sobrecargamos el operador de comma y lo envolvemos += como se muestra a continuación

  X& X::operator, (const A& a) { // declared inside `class X` *this += a; // calls `operator+=` } 

Aquí hay un ejemplo de la documentación de OpenCV ( http://docs.opencv.org/modules/core/doc/basic_structures.html#mat ). El operador de coma se usa para la inicialización de cv :: Mat:

 // create a 3x3 double-precision identity matrix Mat M = (Mat_(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); 

Uso el operador de coma para imprimir la salida de registro. En realidad, es muy similar a ostream::operator<< pero encuentro que el operador de coma realmente es mejor para la tarea.

Así que tengo:

 template  MyLogType::operator,(const T& data) { /* do the same thing as with ostream::operator<<*/ } 

Tiene estas bonitas propiedades

  • El operador de coma tiene la prioridad más baja. Entonces, si quieres transmitir una expresión, las cosas no se estropean si olvidas el paréntesis. Comparar:

     myLog << "The mask result is: " << x&y; //operator precedence would mess this one up myLog, "The result is: ", x&y; 

    incluso puede mezclar operadores de comparaciones dentro sin ningún problema, por ejemplo

     myLog, "a==b: ", a==b; 
  • El operador de coma es visualmente pequeño. No se equivoca con la lectura cuando pega muchas cosas juntas

     myLog, "Coords=", g, ':', s, ':', p; 
  • Se alinea con el significado del operador de coma, es decir, "imprimir esto" y luego "imprimir eso".