When to Overload the Comma Operator?

不羁的心 提交于 2019-12-17 03:24:57

问题


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.


回答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



回答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;



回答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.)




回答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.




回答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;



回答6:


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.




回答7:


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...)




回答8:


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?




回答9:


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+=`
  }



回答10:


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".




回答11:


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);


来源:https://stackoverflow.com/questions/5602112/when-to-overload-the-comma-operator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!