在这里读到一些关于转换运算符和构造器的问题,让我思考了它们之间的交互,也就是说,当有一个“模糊”调用时。考虑以下代码:
class A;
class B {
public:
B(){}
B(const A&) //conversion constructor
{
cout << "called B's conversion constructor" << endl;
}
};
class A {
public:
operator B() //conversion operator
{
cout << "called A's conversion operator" << endl;
return B();
}
};
int main()
{
B b = A(); //what should be called here? apparently, A::operator B()
return 0;
}
上面的代码显示“调用A的转换运算符”,这意味着转换运算符是被调用的,而不是构造函数。如果您从A
中删除/注释掉operator B()
代码,编译器将很高兴地切换到使用构造函数(不需要对代码进行其他更改)。
我的问题是:
B b = A();
是一个模棱两可的调用,所以这里一定有某种类型的优先级在起作用。这一先例究竟是在哪里建立的?(引用C++标准是appreciated)A
对象应该如何成为B
对象,是A
还是B
?根据C++的说法,答案是A
--在面向对象的实践中,有没有什么东西表明应该是这样的呢?对我个人来说,无论哪种方式都是有意义的,所以我很想知道这个选择是如何做出的。提前感谢
发布于 2009-09-05 19:05:23
您确实复制了初始化,并且在转换序列中被认为执行转换的候选函数是转换函数和转换构造函数。这些都在你的箱子里
B(const A&)
operator B()
现在,这就是你声明它们的方式。重载解析对此进行了抽象,并将每个候选对象转换为与调用的参数相对应的参数列表。这些参数包括
B(const A&)
B(A&)
第二个原因是转换函数是成员函数。A&
是所谓的隐式对象参数,它是在候选函数是成员函数时生成的。现在,该参数的类型为A
。绑定隐式对象参数时,非常数引用可以绑定到右值。因此,另一个规则是,当你有两个可行的函数,其参数是引用,那么拥有最少常量资格的候选人将获胜。这就是你的转换函数获胜的原因。尝试将operator B
设置为常量成员函数。你会注意到模棱两可的。
从面向对象的哲学观点来看,这是代码应该表现的方式吗?谁知道更多关于A对象如何变成B对象的问题,A还是B?根据C++的说法,答案是A --在面向对象的实践中,有什么东西表明应该是这样的吗?对我个人来说,无论哪种方式都是有意义的,所以我很想知道这个选择是如何做出的。
根据记录,如果您将转换函数设置为常量成员函数,那么GCC将选择构造函数(所以GCC似乎认为B
与它有更多的关系?)。切换到pedantic模式(-pedantic
)以使其导致诊断。
标准文本,8.5/14
否则(即,对于剩余的复制-初始化情况),如13.3.1.4中所述枚举可以从源类型转换为目标类型或者(当使用转换函数时)转换为其派生类的用户定义的转换序列,并且通过重载解决(13.3)来选择最佳转换序列。
和13.3.1.4
重载解析用于选择要调用的用户定义的转换。假设"cv1 T“是正在初始化的对象的类型,T是类类型,候选函数的选择如下:
在这两种情况下,参数列表都有一个参数,即初始化器表达式。注意:此参数将与构造函数的第一个参数和转换函数的隐式对象参数进行比较。
和13.3.3.2/3
发布于 2009-09-05 19:27:58
似乎MSVS2008对构造函数的选择有自己的看法:它在B中调用复制构造函数,而不考虑A的运算符的常量。因此,即使标准指定了正确的行为,也要小心。
我以为MSVS只是在转换运算符之前搜索合适的构造函数,但后来发现如果从B的构造函数中删除const word,它就会开始调用A的运算符B()。可能它对临时函数有一些特殊的行为,因为下面的代码仍然调用B的构造函数:
A a;
B b = a;
https://stackoverflow.com/questions/1384007
复制相似问题