32

看这个例子:

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}很好。大括号被转换为 aFoo以匹配隐式operator=方法的参数类型。如果operator=是用户定义的,它仍然有效。

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

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

4

5 回答 5

41

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

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

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

— 作为新表达式中的初始值设定项 (5.3.4)

— 在返回语句中 (6.6.3)

— 作为 for-range-initializer (6.5)

作为函数参数(5.2.2)

— 作为下标 (5.2.1)

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

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

— 在 mem-initializer (12.6.2) 中

在作业的右侧(5.17)

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

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

if (a.operator == ({1, 2}))
于 2013-04-23T12:21:35.560 回答
7

只是根本不支持。

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

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

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

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

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

于 2013-04-23T12:22:23.710 回答
4

需要显式转换。

if (a == (Foo){1, 2})
{
}
于 2013-04-23T12:22:49.127 回答
0

没有真正的原因。

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

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

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

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

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

将被不同地处理,因此有一个特殊的分配规定以允许语法。

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

编辑

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

于 2013-04-23T13:03:09.917 回答
0

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

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

其中 b 是类 Foo 的对象

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

于 2013-04-23T12:23:25.037 回答