Когда перегружать оператор запятой? - PullRequest
63 голосов
/ 09 апреля 2011

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

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

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

foo, bar;

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

Ответы [ 11 ]

129 голосов
/ 09 августа 2013

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

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;
59 голосов
/ 09 апреля 2011

Давайте немного изменим акцент на:

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

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

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

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

  • Boost.Assign
  • Boost.Phoenix - здесь принципиально важно то, что он позволяет лямбдам-фениксам поддерживать несколько операторов
34 голосов
/ 09 апреля 2011

Boost.Assign использует его, чтобы позволить вам делать такие вещи, как:

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

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


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

22 голосов
/ 12 августа 2011

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

13 голосов
/ 09 апреля 2011

Аналогично @ GMan's 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;
9 голосов
/ 14 марта 2013

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

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

Из обоснования FAQ :

В: Перегруженный оператор запятой - просто запутывание, мне не нравится.

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

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

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

6 голосов
/ 09 апреля 2011

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

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

5 голосов
/ 01 января 2013

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

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

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

4 голосов
/ 04 августа 2014

Одним из практических применений является эффективное использование его с переменными аргументами в макросе. Кстати, переменные аргументы ранее были расширением в GCC, а теперь являются частью стандарта C ++ 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+=`
  }
2 голосов
/ 22 августа 2018

Я использую оператор запятой для вывода журнала. На самом деле это очень похоже на ostream::operator<<, но я нахожу оператор запятой на самом деле лучше для задачи.

Итак, у меня есть:

template <typename T>
MyLogType::operator,(const T& data) { /* do the same thing as with ostream::operator<<*/ }

Обладает такими приятными свойствами

  • Оператор запятой имеет самый низкий приоритет. Так что, если вы хотите передать выражение, все не испортится, если вы забудете скобки. Сравните:

    myLog << "The mask result is: " << x&y; //operator precedence would mess this one up
    myLog, "The result is: ", x&y;
    

    вы даже можете без проблем смешивать операторы сравнения внутри, например,

    myLog, "a==b: ", a==b;
    
  • Оператор запятой визуально мал. Не скучает по чтению при склеивании многих вещей

    myLog, "Coords=", g, ':', s, ':', p;
    
  • Выравнивается с , означающим оператора запятой, то есть «напечатайте это», а затем «напечатайте это».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...