何时重载逗号运算符?

问题描述 投票:0回答:11

我经常看到关于在 C++ 中重载逗号运算符的问题(主要与重载本身无关,但类似于序列点的概念),这让我想知道:

何时应该使用逗号?它的实际用途有哪些例子?

我只是想不出任何我见过或需要的例子

foo, bar;

在现实世界的代码中,所以我很好奇何时(如果有的话)实际使用它。

c++ function operator-overloading comma-operator
11个回答
181
投票

我使用了逗号运算符来索引具有多个索引的映射。

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;
弃用通知
在数组下标中使用逗号运算符在 C++20 中已弃用,并在 C++23 中删除。
请参阅有关逗号运算符的 cppreference 文章

85
投票

让我们稍微改变一下重点:

什么时候应该you超载逗号?

答案:从来没有。

例外:如果您正在进行模板元编程,

operator,
在运算符优先级列表的最底部有一个特殊的位置,这可以在构建 SFINAE-guards 等时派上用场。

我见过的重载的唯一两个实际用途

operator,
都在Boost中:


40
投票

Boost.Assign 使用它,让您可以执行以下操作:

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

我已经看到它用于奇怪的语言黑客,我会看看是否能找到一些。


啊哈,我确实记得那些奇怪的用途之一:收集多个表达式。 (警告,黑魔法。)


23
投票

逗号有一个有趣的属性,它可以采用

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,
与右侧关联。


16
投票

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

14
投票

我使用逗号运算符来打印日志输出。它实际上与

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;
    
  • 它与逗号运算符的含义一致,即“打印这个”然后“打印那个”。


10
投票

SOCI - C++ 数据库访问库中,它用于接口入站部分的实现:

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

来自基本原理常见问题解答

问:重载的逗号运算符只是混淆,我不喜欢它。

好吧,请考虑以下几点:

“将查询 X 发送到服务器 Y 并将结果放入变量 Z 中。”

上面的“and”起到了逗号的作用。即使重载逗号运算符在 C++ 中并不是一种非常流行的做法,一些库也会这样做,从而实现简洁且易于学习的语法。我们非常确定,在 SOCI 中,逗号运算符被重载,效果很好。


6
投票

一种可能性是Boost分配库(尽管我很确定有些人会认为这种滥用而不是很好的用途)。

Boost Spirit 可能也会重载逗号运算符(它几乎重载其他所有内容......)


5
投票

实际用途之一是在宏中有效地使用可变参数。顺便说一句,变量参数早期是 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+=`
  }

5
投票

同样,我收到了一个带有逗号运算符重载的 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");

3
投票

这是 OpenCV 文档中的示例 (http://docs.opencv.org/modules/core/doc/basic_structs.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);
© www.soinside.com 2019 - 2024. All rights reserved.