When to Overload the Comma Operator?

C++FunctionOperator Overloading

C++ Problem Overview


I see questions on SO every so often about overloading the comma operator in C++ (mainly unrelated to the overloading itself, but things like the notion of sequence points), and it makes me wonder:

When should you overload the comma? What are some examples of its practical uses?

I just can't think of any examples off the top of my head where I've seen or needed to something like

foo, bar;

in real-world code, so I'm curious as to when (if ever) this is actually used.

C++ Solutions


Solution 1 - C++

I have used the comma operator in order to index maps with multiple indices.

enum Place {new_york, washington, ...};

pair<Place, Place> operator , (Place p1, Place p2)
{
	return make_pair(p1, p2);
}


map< pair<Place, Place>, double> distance;

distance[new_york, washington] = 100;

Solution 2 - C++

Let's change the emphasis a bit to:

>When should you overload the comma?

The answer: Never.

The exception: If you're doing template metaprogramming, operator, has a special place at the very bottom of the operator precedence list, which can come in handy for constructing SFINAE-guards, etc.

The only two practical uses I've seen of overloading operator, are both in Boost:

Solution 3 - C++

Boost.Assign uses it, to let you do things like:

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

And I've seen it used for quirky language hacks, I'll see if I can find some.


Aha, I do remember one of those quirky uses: collecting multiple expressions. (Warning, dark magic.)

Solution 4 - C++

The comma has an interesting property in that it can take a parameter of type void. If it is the case, then the built-in comma operator is used.

This is handy when you want to determine if an expression has type void:

namespace detail_
{
    template <typename T>
    struct tag
    {
        static T get();
    };

    template <typename T, typename U>
    tag<char(&)[2]> operator,(T, tag<U>);

    template <typename T, typename U>
    tag<U> operator,(tag<T>, tag<U>);
}

#define HAS_VOID_TYPE(expr) \
    (sizeof((::detail_::tag<int>(), \
             (expr), \
             ::detail_::tag<char>).get()) == 1)

I let the reader figure out as an exercise what is going on. Remember that operator, associates to the right.

Solution 5 - C++

Similar to @GMan's Boost.Assign example, Blitz++ overloads the comma operator to provide a convenient syntax for working with multidimensional arrays. For example:

Array<double,2> 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;

Solution 6 - C++

In SOCI - The C++ Database Access Library it is used for the implementation of the inbound part of the interface:

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

From the rationale FAQ:

> Q: Overloaded comma operator is just obfuscation, I don't like it. > > Well, consider the following: > > "Send the query X to the server Y and put result into variable Z." > > Above, the "and" plays a role of the comma. Even if overloading the comma operator is not a very popular practice in C++, some libraries do this, achieving terse and easy to learn syntax. We are pretty sure that in SOCI the comma operator was overloaded with a good effect.

Solution 7 - C++

I use the comma operator for printing log output. It actually is very similar to ostream::operator<< but I find the comma operator actually better for the task.

So I have:

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

It has these nice properties

  • The comma operator has the lowest priority. So if you want to stream an expression, things do not mess up if you forget the parenthesis. Compare:

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

    you can even mix comparisons operators inside without a problem, e.g.

      myLog, "a==b: ", a==b;
    
  • The comma operator is visually small. It does not mess up with reading when gluing many things together

      myLog, "Coords=", g, ':', s, ':', p;
    
  • It aligns with the meaning of the comma operator, i.e. "print this" and then "print that".

Solution 8 - C++

One possibility is the Boost Assign library (though I'm pretty sure some people would consider this abuse rather than a good use).

Boost Spirit probably overloads the comma operator as well (it overloads almost everything else...)

Solution 9 - C++

Along the same lines, I was sent a github pull request with comma operator overload. It looked something like following

class Mylogger {
    public:
            template <typename T>
            Mylogger & operator,(const T & val) {
                    std::cout << val;
                    return * this;
            }
 };

 #define  Log(level,args...)  \
    do { Mylogger logv; logv,level, ":", ##args; } while (0)

then in my code I can do:

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

Can someone explain why this is a good or bad usage case?

Solution 10 - C++

One of the practical usage is for effectively using it with variable arguments in macro. By the way, variable arguments was earlier an extension in GCC and now a part of C++11 standard.

Suppose we have a class X, which adds object of type A into it. i.e.

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

What if we want to add 1 or more objects of A into X buffer;?
For example,

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

Above macro, if used as:

ADD(buffer, objA1, objA2, objA3);

then it will expand to:

buffer += objA1, objeA2, objA3;

Hence, this will be a perfect example of using comma operator, as the variable arguments expand with the same.

So to resolve this we overload comma operator and wrap it around += as below

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

Solution 11 - C++

Here is an example from OpenCV documentation (http://docs.opencv.org/modules/core/doc/basic_structures.html#mat). The comma operator is used for cv::Mat initialization:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser541686View Question on Stackoverflow
Solution 1 - C++PetterView Answer on Stackoverflow
Solution 2 - C++ildjarnView Answer on Stackoverflow
Solution 3 - C++GManNickGView Answer on Stackoverflow
Solution 4 - C++Alexandre C.View Answer on Stackoverflow
Solution 5 - C++Josh KelleyView Answer on Stackoverflow
Solution 6 - C++moooeeeepView Answer on Stackoverflow
Solution 7 - C++CygnusX1View Answer on Stackoverflow
Solution 8 - C++Jerry CoffinView Answer on Stackoverflow
Solution 9 - C++AC.View Answer on Stackoverflow
Solution 10 - C++iammilindView Answer on Stackoverflow
Solution 11 - C++Aleksei PetrenkoView Answer on Stackoverflow