对于用户定义类型,是否禁止对用户定义 operator= 的 lhs 使用转换运算符?如果是这样,标准中的哪一部分禁止它?

Is the use of conversion operator forbidden for the lhs of user-defined operator= for user-defined types? If so, what part of the standars forbids it?

提问人:Enlico 提问时间:3/10/2023 最后编辑:Enlico 更新时间:3/18/2023 访问量:86

问:

以一个简单的类为例,包装一个 ,int

struct Foo {
    int x;
} f;

以及一个包含 和 可以转换为它的类,Foo

struct Bar {
    Foo f;
    operator Foo&() {
        return f;
    }
    operator Foo const&() const {
        return f;
    }
    Bar& operator=(Bar const&) = default;
} b;

标准的哪一部分(如果有的话)使它无效

b = f;

而不是等同于这个?

static_cast<Foo&>(b) = f;

(我不是说它应该,也不是说它会是正常的、预期的或类似的东西。

这里我读到了

对于内置赋值运算符,左操作数的转换受到如下限制:

  • [...]
  • 不会对左操作数应用任何用户定义的转换,以实现与内置候选项的最左侧参数的类型匹配。

对于所有其他运营商,此类限制不适用。

那么我是否误解了“内置赋值运算符”的含义,而 in 是适用该限制的内置函数?=b = f=

或者这不是 bult-in,因此限制不适用,但代码由于其他原因存在缺陷?=

C++ 语言律师 C++20 赋值运算 转换运算符

评论

0赞 Ben Voigt 3/10/2023
你建议匹配正确吗?复制赋值运算符,它不是内置的,但由编译器默认。Foo& Foo::operator=(const Foo&)
1赞 Nicol Bolas 3/10/2023
"而不是等同于这个?我不明白为什么要考虑它。没有什么可以“禁止”它;这根本不被认为是一种可能性。
1赞 Eljay 3/10/2023
如果你想能够做到这一点,你可以添加一个构造函数。b = f;Bar(Foo const& foo) : f{foo} {}

答:

2赞 Ben Voigt 3/10/2023 #1

您正在考虑的复制赋值运算符不是“内置”运算符,但将操作数转换为重载运算符也受到限制,如下所示:

转换为隐式对象参数或转换为赋值操作的左操作数时,仅允许使用标准转换序列。

https://eel.is/c++draft/over.best.ics#general-9

本说明中也提到了这一点

注 1:由于在转换为赋值操作 ([over.best.ics]) 的左操作数时仅考虑标准转换序列,因此具有类类型的子表达式的表达式始终被解释为 。x = yxx.operator=(y)

https://eel.is/c++draft/over.oper#over.ass-note-1

4赞 Barry 3/10/2023 #2

第 3 段描述了候选集:

  • 对于操作数类型为 cv1 T1 的一元运算符 @,以及左操作数为 cv1 T1 且右操作数类型为 cv2 T2 的二元运算符 @,四组候选函数、指定成员候选函数、非成员候选函数、内置候选函数和重写候选函数构造如下:
    1. 如果 是完整的类类型或当前正在定义的类,则成员候选集是在 的作用域中搜索的结果;否则,成员候选项集为空。T1operator@T1
    2. 对于运算符 、 或 ,非成员候选项的集合为空;否则 [...]=[]->

因此,对于,我们的会员候选人是,我们没有非会员候选人。b = fb.operator=(f)

您引用的段落谈到了内置候选者何时可行,但内置的候选项是由语言提供的(在 [over.built] 中)——如果您的类型可转换为类似 .这些在这里无关紧要。int&

但在这种情况下,可以转换为 ,并且这里没有可以考虑作为候选的机制。我们只有成员候选人要考虑,而且只有我们自己的(的)成员 - 而不是任意其他类型的成员。bFoo&Foo::operator=Bar