【问题标题】:Why can I use operator= but not operator== with C++11 brace-initializers?为什么我可以使用 operator= 而不是 operator== 与 C++11 大括号初始化器?
【发布时间】:2013-04-23 12:17:47
【问题描述】:

看这个例子:

struct Foo
{
    int a;
    int b;

    bool operator == (const Foo & x)
    {
        return a == x.a && b == x.b;
    }
};

int main ()
{
    Foo a;

    a = {1, 2};

    if (a == {1, 2}) // error: expected primary-expression before ‘{’ token
    {
    }
}

a={1,2} 行很好。大括号转换为Foo 以匹配隐式operator= 方法的参数类型。如果 operator= 是用户定义的,它仍然有效。

if (a=={1,2}}) 行错误,如图所示。

为什么表达式{1,2} 不能转换为Foo 以匹配用户定义的operator== 方法?

【问题讨论】:

  • 这是g++-4.7。
  • 我在您的代码中没有看到任何明确的operator==。您的意思是“用户定义”吗?
  • 它被称为初始化列表而不是表达式 litteral 的事实可能表明它不是故意支持的(尽管在你问之前我没有考虑过)。跨度>
  • 你可以改写if (a == Foo{1, 2})
  • 我已经开始讨论,希望在未来的版本中可以取消这个限制。免费添加评论。 groups.google.com/a/isocpp.org/d/msg/std-proposals/nXjimf0amus/…

标签: c++ c++11


【解决方案1】:

列表初始化不能用作运算符的参数在一般情况下。根据 C++11 标准的第 8.5.4/1 段:

[...] 可以使用列表初始化

——作为变量定义中的初始化器(8.5)

——作为新表达式 (5.3.4) 中的初始化器

——在 return 语句中 (6.6.3)

——作为for-range-initializer (6.5)

作为函数参数 (5.2.2)

——作为下标(5.2.1)

——作为构造函数调用的参数(8.5、5.2.3)

——作为非静态数据成员的初始化器 (9.2)

——在内存初始化器 (12.6.2) 中

在作业的右侧 (5.17)

最后一项解释了为什么在operator = 的右侧允许列表初始化,尽管对于任意运算符通常是不允许的。

但是,由于上面的第五项,它可以用作常规函数调用的参数,这样:

if (a.operator == ({1, 2}))

【讨论】:

  • @6502:因为标准是这么说的(见我引用的段落)
  • 真的没那么有趣。 尝试编写一个强大且明确的语言功能来支持它。
  • @6502:我猜这就是所谓的“非建设性”问题。我说不出理由。也许委员会的某个人可以,或者比我更有经验/洞察力的用户
  • if(a.operator==({a,b})) 工作,顺便说一句。我想这意味着这两种形式并不像我想象的那样等价。
  • @spraff: 不,表达式(根据标准的第 5 条)是可以评估并产生具有类型和可能副作用的值的东西(当然,表达式还需要一些语法以便编写,但这不是我的意思)。另一方面,{a, b} 不是一个表达式——它没有类型,它只是一个用于初始化的语言结构。它的元素ab 是表达式,但{a, b} 不是
【解决方案2】:

只是不支持而已。

初始化器列表在initializations ([C++11: 8.5.4]) 和assignments 中明确定义为有效:

[C++11: 5.17/9]:braced-init-list 可能出现在

的右侧
  • 对标量的赋值,在这种情况下,初始化列表最多只能有一个元素。 x={v} 的含义,其中T 是表达式x 的标量类型,是x=T(v) 的含义,除了不允许缩小转换(8.5.4)。 x={}的含义是x=T()
  • 由用户定义的赋值运算符定义的赋值,在这种情况下,初始化列表作为参数传递给运算符函数。

没有允许其他任意情况的标准措辞。

如果允许,在本例中,{1,2} 的类型将相当模糊。这将是一个复杂的语言特性。

【讨论】:

  • operator=operator== 之间的主要逻辑区别是什么?它们都可以将任意类型作为 RHS
  • 我猜他们将赋值视为一种特殊情况,因为它与初始化相似。
  • 我猜它可以被定义为具有语句上下文所需的任何类型。我们已经决定一个数字是 int、float 还是构造函数的参数等。拥有{1,2} 本身不需要任何意义。
  • @6502 不要问“逻辑差异”。 这是 C++。 C++ 不是一种设计,而是一个滚雪球,不断积累 hack。
【解决方案3】:

需要显式转换。

if (a == (Foo){1, 2})
{
}

【讨论】:

    【解决方案4】:

    当您使用包含用户定义类型 a 和 == 运算符的表达式时 调用重载的运算符函数,根据您给出的定义,该函数需要 Foo 类对象的引用的参数

    所以你的表达应该像 a==b

    其中 b 是类 Foo 的对象

    使用这个表达式,您将能够比较 b 和 a 的数据成员,从而知道它们是否相等

    【讨论】:

      【解决方案5】:

      没有真正的原因。

      C++ 是委员会努力的结果,因此有时会因为复杂的政治/社会学动态而做出奇怪但经过深思熟虑的决定。

      C++ 语法很难。很难。几乎难以置信的艰难。甚至有一些规则类似于“如果你可以将这个任意长的标记序列解析为这个或那个,那么就是这个”。

      编译器花了很多年的时间才简单地同意什么是 C++,什么不是。

      在这种情况下,我的疯狂猜测是他们不喜欢看起来非常相似的案例:

      MyClass x = {...};
      MyClass y; y = {...};
      

      会以不同的方式处理,因此有一个特殊的分配规定以允许语法。

      从技术角度来看,我看不出允许其他运算符也有什么问题,另一方面,如果有问题(例如重载、模板实例化等)我看不到分配如何希望逃脱他们。

      编辑

      g++ 不仅允许使用严格的operator=,还允许使用operator+=operator-= 和类似的“增强赋值”。可能只有当您允许非成员重载(禁止赋值和扩充赋值运算符)时才会出现逻辑问题。

      【讨论】:

        猜你喜欢
        • 2018-04-06
        • 1970-01-01
        • 2021-12-19
        • 2016-03-03
        • 2016-12-20
        相关资源
        最近更新 更多