何时超载逗号运算符?

Meh*_*dad 64 c++ function operator-overloading

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

你什么时候应该重写逗号?它的实际用途有哪些例子?

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

foo, bar;
Run Code Online (Sandbox Code Playgroud)

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

Pet*_*ter 134

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

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;
Run Code Online (Sandbox Code Playgroud)

  • 我其实非常喜欢这个,+1. (27认同)
  • 另一方面,这是为了克服我们只能将一个参数传递给`operator []`的事实.有些人建议它可以采取几个参数:见[进化缺陷报告88](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4325.html#88). (10认同)
  • `distance [{new_york,washington}]`无需重载任何东西.额外的一组括号是一个很小的代价,以避免一些如此邪恶的东西! (7认同)
  • 如果你调用一个函数 ``foo(new_york, Washington)```,它应该有两个单独的位置作为参数,会发生什么? (6认同)
  • 从 C++23 (&lt;https://wg21.link/p2128&gt;) 开始,这是不必要的,它支持多参数“operator[]”。 (5认同)
  • 感觉这也是一种很好的语法,也可以用于多维数组实现,但不幸的是,对于整数类型来说,重载不是很好. (2认同)

ild*_*arn 61

让我们把重点改为:

什么时候应该重写逗号?

答案:永远不会.

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

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

  • 永远不要把话说绝了. (82认同)
  • 但是异常+1.:P你是否介意在模板元编程中使用`operator,`进行详细说明?听起来真的很有趣. (6认同)
  • 另外,Boost.Parameter重载了逗号运算符,这是另一种用法。另外,我同意逗号运算符几乎永远不会过载。由于优先级较低,因此难以有效使用。 (2认同)
  • 您也可以在 Eigen 中找到它。 (2认同)

GMa*_*ckG 34

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

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;
Run Code Online (Sandbox Code Playgroud)

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


啊哈,我确实记得其中一个古怪的用法:收集多个表达式.(警告,黑魔法.)

  • ......也许不是最明智的选择之一,因为在向量上至少有两个对该运算符同等有效的解释.我假设"将元素附加到结尾"就是这里的意思,但它同样可以"在带有这些参数的向量中的每个元素上调用operator + =".它可能只适用于相同大小的集合,或者它可能会对较小的集合进行零扩展,或者无论如何......你不知道如果没有深入研究文档,这一点并不明显.好的代码很明显没有解释. (5认同)
  • 好吧,运算符+ =在共同的理解中添加了右侧表达式的值.表达式1,2,... 9在共同理解中评估为9.重载运算符会颠覆语义,虽然它在语法上是有效的,但这并不意味着它必然是好的.运算符重载是好的,如果它使代码清晰,但在这里它使代码模糊和混乱(至少在我的感觉).它与例如C++ 0x中的initializer_list赋值有很大的不同,因为花括号使得它立即显而易见.另外,我认为重载operator + = for vector ... (2认同)
  • 作为另一个例子,我记得几年前在一个字符串类中运行,它重载了`operator <=`.这允许你编写酷的代码,如`str <="foo";`.当读下你的代码的下一个人说"到底是怎么回事?"时,它根本不酷.当你第一次花一周的时间调试什么都没有因为有人不知道并且写了一些像`if(str <="bar")`之类的东西时,它变得完全不酷. (2认同)

Ale*_* C. 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)
Run Code Online (Sandbox Code Playgroud)

我让读者想出一个正在进行的练习.请记住,operator,右边的同事.

  • “作为练习,我让读者弄清楚发生了什么。” 不是答案网站的重点。 (3认同)

Jos*_*ley 13

@ 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;
Run Code Online (Sandbox Code Playgroud)


moo*_*eep 9

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

sql << "select name, salary from persons where id = " << id,
       into(name), into(salary);
Run Code Online (Sandbox Code Playgroud)

理论常见问题解答:

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

好吧,请考虑以下事项:

"将查询X发送到服务器Y并将结果放入变量Z."

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


Cyg*_*sX1 8

我使用逗号运算符来打印日志输出。它实际上非常类似于ostream::operator<<但我发现逗号运算符实际上适合该任务。

所以我有:

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

它有这些不错的特性

  • 逗号运算符的优先级最低。所以如果你想流式传输一个表达式,如果你忘记了括号,事情就不会搞砸了。相比:

    myLog << "The mask result is: " << x&y; //operator precedence would mess this one up
    myLog, "The result is: ", x&y;
    
    Run Code Online (Sandbox Code Playgroud)

    您甚至可以在内部混合比较运算符而不会出现问题,例如

    myLog, "a==b: ", a==b;
    
    Run Code Online (Sandbox Code Playgroud)
  • 逗号运算符在视觉上很小。将许多东西粘在一起时,不会影响阅读

    myLog, "Coords=", g, ':', s, ':', p;
    
    Run Code Online (Sandbox Code Playgroud)
  • 它与逗号运算符的含义一致,即“打印这个”然后“打印那个”。


Jer*_*fin 6

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

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


AC.*_*AC. 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)
Run Code Online (Sandbox Code Playgroud)

那么在我的代码中,我可以做到:

 Log(2, "INFO: setting variable \", 1, "\"\n");
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为现代C++会使用可变参数. (4认同)

iam*_*ind 5

实际用途之一是在宏中有效地使用可变参数。顺便说一句,变量参数早期是 GCC 中的扩展,现在是 C++11 标准的一部分。

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

class X {
  public: X& operator+= (const A&);
};
Run Code Online (Sandbox Code Playgroud)

A如果我们想向 中添加 1 个或多个对象怎么办X buffer;
例如,

#define ADD(buffer, ...) buffer += __VA_ARGS__
Run Code Online (Sandbox Code Playgroud)

上述宏,如果用作:

ADD(buffer, objA1, objA2, objA3);
Run Code Online (Sandbox Code Playgroud)

那么它将扩展为:

buffer += objA1, objeA2, objA3;
Run Code Online (Sandbox Code Playgroud)

因此,这将是使用逗号运算符的完美示例,因为变量参数也以相同的方式扩展。

因此,为了解决这个问题,我们重载comma运算符并将其包装+=如下

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