When to Overload the Comma Operator?
Solution 1
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.Assign
- Boost.Phoenix – it's fundamental here in that it allows Phoenix lambdas to support multiple statements
Solution 2
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 3
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
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
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;
Related videos on Youtube
user541686
Updated on August 23, 2020Comments
-
user541686 over 3 years
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.
-
GManNickG about 13 yearsMeh, can't find it. Very corner-case stuff.
-
user541686 about 13 years+1 Whoa I hadn't seen that before, cool! (Though I doubt I'll be using it often, haha.)
-
user541686 about 13 yearsBut +1 for the exception. :P Would you mind elaborating a bit on the template metaprogramming use of
operator,
? It sounds really interesting. -
user541686 about 13 yearsDefinitely interesting libraries! +1
-
ildjarn about 13 years@Mehrdad : It'd be too long to put in a comment, and I don't recall offhand any articles about it online except maybe a few by Eric Niebler (check his website, they're all worth a read). In my personal experience, I've never run across an instance where I couldn't use overload resolution +
...
to solve my problem instead (which I personally find easier to understand than operator precedence +operator,
). -
Damon about 13 yearsBut seriously, would you really want to write code like this? To someone reading your code, this will be utterly confusing. I assume that snippet is a shorthand for a
push_back
on those 8 values, but it looks like 9 is being added to avector<int>
, which doesn't make any sense. Frankly, this is a strong counter argument for Boost being a "high quality library". Code should be clear and obvious. Otherwise, one could as well implement something likeT& operator--(int){ delete this; return *this; }
, which would probably work fine, too. It's just not obvious to someone else what happens. -
Xeo about 13 years@Damon: "it looks like 9 is being added" - it not only looks like that, 9 itself also gets added. Why wouldn't it?
-
Damon about 13 yearsWell, operator+= adds, in common understanding, the value of the expression on the right side. The expression 1,2,...9 evaluates to 9 in common understanding. Overloading operators subverts the semantics, and while it is syntactically valid, that does not mean it is necessarily good. Operator overloading is good if it makes code clear, but here it makes code ambiguous and confusing (at least in my feeling). It is much different with e.g. initializer_list assignment in C++0x because the curly braces make it immediately obvious what's going on. Also, I deem overloading operator+= for a vector...
-
Damon about 13 years... as maybe not one of the wisest choices, because there are at least two equally valid interpreations of that operator on a vector. I assume that "append element(s) to end" is what's meant here, but it could equally well be "invoke operator += on each element in vector with these arguments". It might very well be defined only for sets of equal size, or it might zero-extend the smaller set, or whatever... thing is, you don't know without intensely studying documentation, it is not obvious. Good code is obvious without explanation.
-
Damon about 13 yearsAs another example, I remember running across a string class a few years ago which overloaded
operator<=
. That allowed you to write cool code likestr <= "foo";
. Except it isn't cool at all when the next person reading your code says "what the hell?" and it becomes totally uncool the first time you spend a week debugging for nothing because someone didn't know and wrote something likeif(str <= "bar")
. -
Paul Fultz II about 12 yearsAlso, Boost.Parameter overloads the comma operator, which is another use. Also, I agree that the comma operator should almost never be overloaded. Its difficult to use effectively, because of its low precedence.
-
alfC over 10 yearsI don't know if it is bad or not. But avoids writing code like this:
... << "This is a message on line " << std::to_string(__LINE__) << " because variable a = " << std::to_string(a) << " which is larger than " << std::to_string(limit) << "\n"
. Which is very common in reporting errors or building messages for exceptions. I am not sure if comma was the only choice: any other operator could have achieved this, for exampleoperator+
oroperator|
oroperator&&
or evenoperator<<
itself. But it is an interting case. -
Petter over 9 yearsI think modern C++ would use variadic tempates instead.
-
ildjarn over 9 yearsI actually really like this, +1.
-
Morwenn about 9 yearsOn the other hand, this is to overcome the fact that we can only pass one parameter to
operator[]
. Some have proposed that it can take several parameters: see Evolution Defect Report 88. -
WorldSEnder about 9 yearsMaybe by now it should be
template<typename ... A> X& ADD(X& buff, A ... args) { int sink[]={ 0,(void(buff+=args),0)... }; return buff;}
. Note: you probably have to prevent optimization of sink with a(void) sink;
statement. This dodges the macro, which is, imo, even better -
sim642 over 8 yearsIt feels like a great syntax to also use for multidimensional array implementation but unfortunately not so well overloaded for integral types.
-
lmat - Reinstate Monica about 8 yearsIt's bad to answer questions with questions ;-)
-
Pharap almost 7 yearsIgnoring the fact territories change (less than 100 years ago Prussia was still a valid European state), using the comma operator to create tuples is perfectly valid and useful.
-
Gabriel de Grimouard almost 7 yearsyou can also find it in Eigen.
-
Arthur Tacca almost 6 years
distance[{new_york, washington}]
works without overloading anything. An extra set of brackets is a small price to pay to avoid something so evil! -
Petter almost 6 yearsIt is too bad that we can not change the language so that a,b could mean something like std::tie(a, b).
-
HelloGoodbye over 5 yearsI downvoted your answer since it is self-contradictory. First you state that you should never overload the
,
operator, then you write that useful overloads of it do exist. -
ildjarn over 5 years@HelloGoodbye : Except that I never stated that one should never overload it; I stated that you, as the asker of this question, should never overload it. I.e. if you don't already know the answer, then the answer is never – this is not self-contradictory in any way IMO. Thanks for explaining the downvote though. :-]
-
HelloGoodbye over 5 yearsFair enough! ;)
-
Kuba hasn't forgotten Monica about 5 yearsThe low precedence allows composition of almost all imaginable expressions without requiring additional parentheses - that's a very neat property of that operator. It does come handy and over the years I've found plenty of uses for it that made the code readable and expressive... but my rule is to use it only when it introduces no surprises and makes the meaning of the code obvious even to someone who didn't read the documentation of the API in use.
-
OutOfBound over 4 yearsWhat happens if you call a function
foo(new_york, washington)
, that should take two seperate places as arguments? -
infinitezero about 3 years"I let the reader figure out as an exercise what is going on." not the point of an answer site.
-
Alexandre C. about 3 years@infinitezero It's not hard, and you should never use that in production, especially not since five new standards have been out since that answer.
-
phuclv about 3 years
-
Caleth almost 3 yearsI would deprecate Boost.Assign in favour of std::initializer_list, and Boost.Phoenix in favour of native lambdas. They were better than nothing before C++11 tho.
-
Sparkofska over 2 yearsI'd add, that in modern code Variadic Templates make the use of the overloaded comma operator obsolete in most cases.
-
Johan Boulé over 2 yearsTurned around, your answer means that "you" should never develop a Boost library. Have more passion please!
-
ildjarn over 2 years@Johan : Or, have some respect for the practicality of generalizations, please!