When to Overload the Comma Operator?

28,248

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:

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;
Share:
28,248

Related videos on Youtube

user541686
Author by

user541686

Updated on August 23, 2020

Comments

  • user541686
    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
    GManNickG about 13 years
    Meh, can't find it. Very corner-case stuff.
  • user541686
    user541686 about 13 years
    +1 Whoa I hadn't seen that before, cool! (Though I doubt I'll be using it often, haha.)
  • user541686
    user541686 about 13 years
    But +1 for the exception. :P Would you mind elaborating a bit on the template metaprogramming use of operator,? It sounds really interesting.
  • user541686
    user541686 about 13 years
    Definitely interesting libraries! +1
  • ildjarn
    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
    Damon about 13 years
    But 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 a vector<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 like T& operator--(int){ delete this; return *this; }, which would probably work fine, too. It's just not obvious to someone else what happens.
  • Xeo
    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
    Damon about 13 years
    Well, 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
    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
    Damon about 13 years
    As another example, I remember running across a string class a few years ago which overloaded operator<=. That allowed you to write cool code like str <= "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 like if(str <= "bar").
  • Paul Fultz II
    Paul Fultz II about 12 years
    Also, 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
    alfC over 10 years
    I 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 example operator+ or operator| or operator&& or even operator<< itself. But it is an interting case.
  • Petter
    Petter over 9 years
    I think modern C++ would use variadic tempates instead.
  • ildjarn
    ildjarn over 9 years
    I actually really like this, +1.
  • Morwenn
    Morwenn about 9 years
    On 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
    WorldSEnder about 9 years
    Maybe 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
    sim642 over 8 years
    It feels like a great syntax to also use for multidimensional array implementation but unfortunately not so well overloaded for integral types.
  • lmat - Reinstate Monica
    lmat - Reinstate Monica about 8 years
    It's bad to answer questions with questions ;-)
  • Pharap
    Pharap almost 7 years
    Ignoring 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
    Gabriel de Grimouard almost 7 years
    you can also find it in Eigen.
  • Arthur Tacca
    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
    Petter almost 6 years
    It is too bad that we can not change the language so that a,b could mean something like std::tie(a, b).
  • HelloGoodbye
    HelloGoodbye over 5 years
    I 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
    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
    HelloGoodbye over 5 years
    Fair enough! ;)
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica about 5 years
    The 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
    OutOfBound over 4 years
    What happens if you call a function foo(new_york, washington), that should take two seperate places as arguments?
  • infinitezero
    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.
    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
    phuclv about 3 years
  • Caleth
    Caleth almost 3 years
    I 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
    Sparkofska over 2 years
    I'd add, that in modern code Variadic Templates make the use of the overloaded comma operator obsolete in most cases.
  • Johan Boulé
    Johan Boulé over 2 years
    Turned around, your answer means that "you" should never develop a Boost library. Have more passion please!
  • ildjarn
    ildjarn over 2 years
    @Johan : Or, have some respect for the practicality of generalizations, please!

Related