program story

쉼표 연산자를 언제 오버로드해야합니까?

inputbox 2020. 11. 22. 19:26
반응형

쉼표 연산자를 언제 오버로드해야합니까?


나는 C ++에서 쉼표 연산자를 오버로딩하는 것에 대한 질문을 자주 봅니다 (주로 오버로딩 자체와 관련이 없지만 시퀀스 포인트 개념과 같은 것).

언제 쉼표를 오버로드 해야 합니까? 실제 사용의 몇 가지 예는 무엇입니까?

나는 내가 본 적이 있거나 같은 것을 필요로했던 내 머릿속에서 어떤 예도 생각할 수 없다.

foo, bar;

실제 코드에서는 이것이 언제 실제로 사용되는지 궁금합니다.


강조를 약간 변경해 보겠습니다.

언제 쉼표 오버로드 해야 합니까?

답 : 절대로.

예외 : 템플릿 메타 프로그래밍을 수행하는 경우 operator,연산자 우선 순위 목록 맨 아래에 특수한 위치가 있으므로 SFINAE 가드 등을 구성하는 데 유용 할 수 있습니다.

내가 본 두 가지 실용적인 용도 operator,는 모두 Boost입니다 .


여러 인덱스가있는 맵을 인덱싱하기 위해 쉼표 연산자를 사용했습니다.

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;

Boost.Assign은이를 사용하여 다음과 같은 작업을 수행 할 수 있습니다.

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

그리고 나는 그것이 기발한 언어 해킹에 사용되는 것을 보았고, 몇 가지를 찾을 수 있는지 볼 것입니다.


아하, 나는 그 기발한 용도 중 하나 인 여러 표현 수집을 기억 합니다 . (경고, 어둠의 마법.)


쉼표는 유형의 매개 변수를 취할 수 있다는 점에서 흥미로운 특성을 가지고 있습니다 void. 이 경우 내장 된 쉼표 연산자가 사용됩니다.

이것은 표현식에 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)

나는 독자가 무슨 일이 일어나고 있는지 연습으로 이해하게했다. operator,오른쪽에있는 동료를 기억하십시오 .


@GMan의 Boost.Assign 예제 와 유사하게 Blitz ++ 는 쉼표 연산자를 오버로드하여 다차원 배열 작업을위한 편리한 구문 을 제공 합니다. 예를 들면 :

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;

에서 SOCI -는 C ++ 데이터베이스 액세스 도서관 그것은 인터페이스의 인바운드 부분의 구현을 위해 사용된다 :

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

로부터 근거 자주 묻는 질문 :

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.


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


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?


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

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


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

참고URL : https://stackoverflow.com/questions/5602112/when-to-overload-the-comma-operator

반응형