繁体   English   中英

何时重载逗号运算符?

[英]When to Overload the Comma Operator?

我经常在SO上看到有关在C ++中重载逗号运算符的问题(主要与重载本身无关,但有一些类似顺序​​点的概念),这让我感到奇怪:

什么时候应该使逗号超载? 其实际用途有哪些例子?

我只是想不出我已经看到或需要的任何例子,例如

foo, bar;

在现实世界的代码中,所以我很好奇它什么时候被使用。

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

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;

让我们将重点更改为:

要重载逗号?

答案:从不。

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

我所见过的关于重载operator,的仅有的两个实际用途都是在Boost中

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

基本原理常见问题解答

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

好吧,请考虑以下几点:

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

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

一种可能是Boost Assign库(尽管我很确定某些人会认为这种滥用而不是很好的使用)。

Boost Spirit可能还会使逗号运算符重载(它几乎重载了其他所有内容……)

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

有人可以解释为什么这是好事还是坏事?

一种实际用法是有效地将其与宏中的可变参数一起使用。 顺便说一句,变量自变量以前是GCC中的扩展,现在是C ++ 11标准的一部分。

假设我们有一个class X ,它向其中添加了类型A对象。

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

如果我们要将1个或多个A对象添加到X buffer;A怎么办?
例如,

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

我使用逗号运算符来打印日志输出。 它实际上与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; 
  • 它符合逗号运算符的含义 ,即“先打印”然后“先打印”。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM