When to Overload the Comma Operator?
C++FunctionOperator OverloadingC++ 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:
-
Boost.Phoenix – it's fundamental here in that it allows Phoenix lambdas to support multiple statements
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);