我有两个类,其中一个代表一个字符串,另一个可以转换为字符串:
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*)
之间进行选择。为什么不同?
答案 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-qualifiedT
”并调用构造函数 在直接初始化的上下文中使用单个参数 对象类型为“ cv2T
”,显式转换函数也是如此 考虑。 那些未隐藏在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 ]