为什么在c++ 11大括号初始化项中可以使用operator=而不能使用operator== ?

Why can I use operator= but not operator== with C++11 brace-initializers?

本文关键字:operator 可以使 不能 c++ 初始化 为什么      更新时间:2023-10-16

请看下面的例子:

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
    {
    }
}

line a={1,2} is fine。大括号被转换为Foo以匹配隐式operator=方法的参数类型。如果operator=是用户定义的,它仍然工作。

if (a=={1,2}})行出现错误。

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

列表初始化不能作为一般情况下操作符的实参。c++ 11标准第8.5.4/1段:

[…可以使用List-initialization

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

-作为new表达式的初始化式(5.3.4)

-在return语句(6.6.3)

-作为for-range初始化式(6.5)

- 作为函数参数 (5.2.2)

-作为下标(5.2.1)

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

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

-在mems初始化式中(12.6.2)

-赋值操作右侧的 (5.17)

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

但是,由于上面的第五项,它可以用作常规函数调用的参数,如下所示:

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

不支持。

初始化列表被显式定义为在初始化 ([C++11: 8.5.4])和赋值:

中有效。

[C++11: 5.17/9]: A 带括号的初始化列表可能出现在

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

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

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

需要显式强制转换。

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

使用包含用户定义类型a和==操作符的表达式时重载操作符函数被调用,根据你给出的定义,它需要一个类Foo

对象引用的参数。

所以表达式应该是a==b

其中b是类Foo

的对象

通过这个表达式,你可以比较b和a的数据成员,从而知道它们是否相等

没有真正的原因。

c++是一个委员会努力的结果,所以有时奇怪但经过深思熟虑的决定可能会因为复杂的政治/社会动态而被忽略。

c++语法很难。很努力。几乎难以置信的困难。甚至有这样的规则:"如果你可以将这个任意长的令牌序列解析为this或that,那么它就是this"。

编译器甚至花了很多年才简单地就什么是c++、什么不是c++达成一致。

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

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

将以不同的方式处理,因此有一个特殊的赋值条款来允许语法。

从技术角度来看,我看不出允许其他操作符也使用赋值有什么问题,另一方面,如果存在问题(例如重载,模板实例化等),我不知道赋值如何能够逃脱它们。

<标题>编辑

g++不仅允许使用严格的operator=,还允许使用operator+=operator-=和类似的"增广赋值"。可能只有在允许非成员重载(赋值和增广赋值操作符禁止)时才会出现逻辑问题。