歧义赋值操作符

Ambiguous assignment operator

本文关键字:赋值操作符 歧义      更新时间:2023-10-16

我有两个类,其中一个表示字符串,另一个可以转换为字符串:

class A {
public:
  A() {}
  A(const A&) {}
  A(const char*) {}
  A& operator=(const A&) { return *this; }
  A& operator=(const char*) { return *this; }
  char* c;
};
class B {
public:
  operator const A&() const {
    return a;
  }
  operator const char*() const {
    return a.c;
  }
  A a;
};

现在,如果我做

B x;
A y = x;

它触发复制构造函数,编译得很好。但是如果我做

A y;
y = x;

它抱怨赋值有歧义,不能在=(A&)=(char*)之间选择。为什么会有不同呢?

初始化和赋值是有区别的。

初始化时,即:

A y = x;

实际呼叫取决于x的类型。如果它是相同类型的y,那么它将像:

A y(x);

如果没有,就像你的例子一样,它将像:

A y(static_cast<const A&>(x));

编译得很好,因为不再有歧义。

在赋值中没有这种特殊情况,因此没有自动解析歧义。

值得注意的是:

A y(x);

在你的代码中也是有歧义的

有§13.3.1.4/(1.2),只适用于类类型对象的(copy-)初始化,指定如何为您的第一个情况找到候选转换函数:

在8.5中规定的条件下,作为a用户定义的类类型对象的复制初始化类型的初始化表达式转换为被初始化对象的类型。重载解析用于选择要调用的用户定义转换。[…]假设"cv1 T"是初始化对象的类型,T是一个类类型,候选函数选择如下:

  • T的转换构造函数(12.3.1)是候选的功能。

  • 当初始化表达式的类型为类类型时" cv S ", S及其基的非显式转换函数考虑类。初始化要绑定到的临时对象时类型的构造函数的第一个参数"对可能符合cv条件的T的引用",然后调用构造函数在直接初始化的上下文中使用单个参数类型为"cv2 T"的对象,则显式转换函数也为考虑。未隐藏在S中并产生类型的那些其cv-不限定版本与T相同类型或派生类是候选函数。[…]返回"对X的引用"的转换函数返回左值或xvalue,根据引用的类型,类型为X,因此被认为在选择候选函数的过程中产生X

。虽然考虑了operator const char*,但没有包括在候选集合中,因为const char*显然在任何方面都与A不相似。然而,在第二个代码片段中,operator=被作为普通成员函数调用,这就是为什么这个限制不再适用;一旦两个转换函数都在候选集合中,重载解析显然会导致歧义。

注意,对于直接初始化,上述规则也不适用。

B x;
A y(x);

是不规范的。

该结果的更一般形式是在重载解析期间,在一个转换序列中永远不可能有两个用户定义的转换。考虑§13.3.3.1/4:

但是,如果目标是
  • 构造函数的第一个参数或[…]

和构造函数[…]是候选的

  • 13.3.1.3,当参数是类复制初始化第二步中的临时参数时,或
  • 13.3.1.4、13.3.1.5或13.3.1.6(在所有情况下),
不考虑

用户定义的转换序列。 (:规则防止应用多个用户定义转换在重载解析期间,从而避免无限递归。- - -结束注意]