模糊赋值运算符

时间:2015-05-27 10:32:17

标签: c++

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

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*)之间进行选择。为什么不同?

2 个答案:

答案 0 :(得分:5)

初始化和分配之间存在差异。

在初始化中,即:

A y = x;

实际通话取决于x的类型。如果它是y的相同类型,则它将类似于:

A y(x);

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

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

编译很好,因为不再存在歧义。

在作业中没有这种特殊情况,因此不能自动解决歧义。

值得注意的是:

A y(x);

在您的代码中也含糊不清。

答案 1 :(得分:4)

有§13.3.1.4/(1.2),只适用于类复制对象的(复制)初始化,它指定了第一种情况的候选转换函数的找到方式:

  

在8.5中指定的条件下,作为a的一部分   类型的对象的复制初始化,用户定义的   可以调用转换将初始化表达式转换为   正在初始化的对象的类型。过载分辨率用于   选择要调用的用户定义转换。 […] 假如说   “cv1 T”是要初始化的对象的类型,T是一个类   类型,候选函数选择如下:

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

  •   
  • 当初始化表达式的类型是类类型时   “ cv S”,S及其基础的非显式转换函数   课程被考虑。初始化临时绑定时   参数类型的构造函数的第一个参数   “引用可能是cv-qualified T”并调用构造函数   在直接初始化的上下文中使用单个参数   对象类型为“ cv2 T”,显式转换函数也是如此   考虑。 那些未隐藏在S内并产生类型的内容   其cv-nonqualified版本与T的类型相同或是派生的   它的类是候选函数。 [...]转换函数返回“引用X”返回左值或x值,   取决于类型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(在所有情况下),
  •   
     

不考虑用户定义的转换序列。 [注意:这些   规则阻止应用多个用户定义的转换   在重载决策期间,从而避免无限递归。 - 结束    note ]