何时超载逗号运算符?

时间:2011-04-09 00:45:57

标签: c++ function operator-overloading

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

时,你重载逗号?它的实际用途有哪些例子?

我只是想不出任何我已经看过或需要的东西的例子

foo, bar;

在真实世界的代码中,所以我很想知道实际使用它的时间(如果有的话)。

11 个答案:

答案 0 :(得分:127)

我使用逗号运算符来索引具有多个索引的地图。

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;

答案 1 :(得分:59)

让我们将重点改为:

  

什么时候 重载逗号?

答案:从不。

例外:如果您正在进行模板元编程,operator,在运算符优先级列表的最底部有一个特殊位置,这对于构建SFINAE防护等非常方便。

我看到的重载operator,的两个实际用途都在Boost中:

答案 2 :(得分:34)

Boost.Assign使用它,让你做以下事情:

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

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


啊哈,我确实记得其中一个古怪的用法:collecting multiple expressions。 (警告,黑魔法。)

答案 3 :(得分:22)

逗号有一个有趣的属性,它可以采用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,与右侧相关联。

答案 4 :(得分:13)

@GMan's Boost.Assign示例类似,Blitz++重载逗号运算符以提供用于处理多维数组的convenient syntax。例如:

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;

答案 5 :(得分:9)

SOCI - The C++ Database Access Library中,它用于实现接口的入站部分:

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

来自rationale FAQ

  

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

     

好吧,请考虑以下事项:

     

&#34;将查询X发送到服务器Y并将结果放入变量Z中。&#34;

     

以上,&#34;和&#34;扮演逗号的角色。即使重载逗号运算符在C ++中不是一种非常流行的实践,一些库也会这样做,实现简洁易学的语法。我们非常确定在SOCI中,逗号运算符过载效果很好。

答案 6 :(得分:6)

一种可能性是Boost Assign库(虽然我很确定有些人会考虑这种滥用而不是很好用)。

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

答案 7 :(得分: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");

有人可以解释为什么这是一个好的或坏的用例?

答案 8 :(得分:4)

其中一个实际用法是在宏中有效地使用变量参数。顺便说一下,变量参数早先是GCC中的扩展,现在是C ++ 11标准的一部分。

假设我们有一个class X,它会将A类型的对象添加到其中。即。

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

如果我们想将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+=`
  }

答案 9 :(得分:2)

我使用逗号运算符来打印日志输出。它实际上与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 :(得分:1)

以下是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);