Подтвердить что ты не робот

Когда перегружать Comma Operator?

Я так часто вижу вопросы о SO о перегрузке оператора запятой в С++ (главным образом, не связанного с перегрузкой, но, например, понятия точек последовательности), и это заставляет меня задаться вопросом:

Когда вы должны перегружать запятую? Каковы некоторые примеры его практического использования?

Я просто не могу думать о каких-либо примерах с моей головы, где я видел или нуждался в чем-то вроде

foo, bar;

в реальном коде, поэтому мне интересно, когда (если когда-либо) это фактически используется.

4b9b3361

Ответ 1

Немного измените акцент на:

Когда вы перегружаете запятую?

Ответ: Никогда.

Исключение: если вы делаете метапрограммирование шаблона, operator, имеет особое место в самом низу списка приоритетов оператора, что может пригодиться для построения защитных устройств SFINAE и т.д.

Только два практических применения, которые я видел при перегрузке operator,, находятся в Boost:

  • Boost.Assign
  • Boost.Phoenix – это фундаментально здесь, поскольку позволяет Phoenix lambdas поддерживать несколько утверждений

Ответ 2

Я использовал оператор запятой для индексирования карт с несколькими индексами.

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 использует его, чтобы вы могли делать такие вещи, как:

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

И я видел, как он использовался для причудливых языковых хаков, я посмотрю, смогу ли я их найти.


Ага, я помню одно из этих причудливых применений: сбор нескольких выражений. (Предупреждение, темная магия.)

Ответ 4

Запятая имеет интересное свойство, поскольку она может принимать параметр типа 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, связывается вправо.

Ответ 5

Как @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;

Ответ 6

В SOCI - Библиотека доступа к базам данных С++ используется для реализации входящей части интерфейса:

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

Из часто задаваемых вопросов:

Q: Перегруженный оператор запятой - просто обфускация, мне это не нравится.

Ну, рассмотрим следующее:

"Отправьте запрос X на сервер Y и поместите результат в переменную Z."

Выше, "и" играет роль запятой. Даже если перегрузка запятой не является очень популярной практикой в ​​С++, некоторые библиотеки делают это, добиваясь краткого и легко обучаемого синтаксиса. Мы уверены, что в SOCI оператор запятой был сильно нагружен.

Ответ 7

Одна из возможностей - это Boost Assign (хотя я уверен, что некоторые люди рассмотрят это злоупотребление, а не хорошее использование).

Boost Spirit, вероятно, также перегружает оператор запятой (он перегружает почти все остальное...)

Ответ 8

В то же время мне был отправлен запрос на github pull с перегрузкой оператора запятой. Это выглядело следующим образом

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)

то в моем коде я могу сделать:

 Log(2, "INFO: setting variable \", 1, "\"\n");

Может кто-нибудь объяснить, почему это хороший или плохой случай использования?

Ответ 9

Одним из практических применений является эффективное использование его с переменными аргументами в макросе. Кстати, переменные аргументы были ранее расширением в GCC и теперь частью стандарта С++ 11.

Предположим, что у нас есть class X, который добавляет в него объект типа A. то есть.

class X {
  public: X& operator+= (const A&);
};

Что делать, если мы хотим добавить 1 или более объектов A в X buffer;?
Например,

#define ADD(buffer, ...) buffer += __VA_ARGS__

Выше макроса, если используется как:

ADD(buffer, objA1, objA2, objA3);

то он будет расширяться до:

buffer += objA1, objeA2, objA3;

Следовательно, это будет прекрасный пример использования оператора запятой, поскольку переменные аргументы расширяются с тем же.

Итак, чтобы разрешить это, мы перегружаем оператор comma и обертываем его вокруг +=, как показано ниже

  X& X::operator, (const A& a) {  // declared inside `class X`
    *this += a;  // calls `operator+=`
  }

Ответ 10

Вот пример из документации OpenCV (http://docs.opencv.org/modules/core/doc/basic_structures.html#mat). Оператор запятой используется для инициализации cv:: Mat:

// create a 3x3 double-precision identity matrix
Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);