为什么给定的转换运算符不调用构造函数?
struct A {};
struct B
{
B (A* pA) {}
B& operator = (A* pA) { return *this; }
};
template<typename T>
struct Wrap
{
T *x;
operator T* () { return x; }
};
int main ()
{
Wrap<A> a;
B oB = a; // error: conversion from ‘Wrap<A>’ to non-scalar type ‘B’ requested
oB = a; // ok
}
当构建 oB
时,为什么不为 Wrap
调用 B::B(A*)
? [注意:在下一个语句中为 Wrap
调用 B::operator = (A*)
]
struct A {};
struct B
{
B (A* pA) {}
B& operator = (A* pA) { return *this; }
};
template<typename T>
struct Wrap
{
T *x;
operator T* () { return x; }
};
int main ()
{
Wrap<A> a;
B oB = a; // error: conversion from ‘Wrap<A>’ to non-scalar type ‘B’ requested
oB = a; // ok
}
When oB
is constructed then Why B::B(A*)
is NOT invoked for Wrap<T>::operator T ()
? [Note: B::operator = (A*)
is invoked for Wrap<T>::operator T ()
in the next statement]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题是隐式调用的用户定义转换的数量受到标准的限制(为 1)。
意味着两次用户转换:
Wrap::operator A*()
a
上:应在结果上 :B::B(A* )
应该被称为@James Kanze 的解释: 这种语法称为“复制初始化”,实际上相当于
B ob = B(a)
(复制大部分时间都被忽略了)。这与 B ob(a) 不同,后者是“直接初始化”并且可以工作。如果您明确限定其中任何一个,它将起作用,例如:
另一方面,对于第二种情况没有问题:
是简写:
因此只需要一个用户定义的转换,这是允许的。
编辑:
由于评论中需要它(基里尔的回答),我们可以猜测其动机。
链式转换可能很长,非常长,因此:
此外,只要有超过 1 次转换,您就会遇到循环的风险,必须检测到循环(即使可能不需要诊断,并且要遵守实施质量)。
因此,由于需要有一个限制来避免无限长的搜索(可以不指定它,但需要最小值),并且由于超过 1 我们可能会遇到新问题(循环),因此 1 看起来毕竟是一个很好的限制。
The problem is that the number of user-defined conversions that are invoked implicitly is limited (to 1) by the Standard.
implies two user conversions:
a
:Wrap<A>::operator A*()
should be calledB::B(A*)
should be called@James Kanze's explanation: this syntax is called "copy initialization", effectively equivalent to
B ob = B(a)
(with the copy being elided most of the time). This is different fromB ob(a)
which is a "direct initialization" and would have worked.if you explicitly qualify any of this, it will work, for example:
On the other hand, for the second case there is no issue:
is short-hand for:
And thus only one user-defined conversion is required, which is allowed.
EDIT:
Since it's been required in a comment (to Kirill's answer) we can take a guess at the motive.
Chained conversions could be long, very long, and therefore:
Furthermore, as long as there is more than 1 conversion, you run into the risk of having cycles, which would have to be detected (even though diagnostic would probably not be required, and be subject to Quality Of Implementation).
So, since a limit is necessary to avoid infinitely long searches (it could have been left unspecified, with a minimum required), and since beyond 1 we may have new issues (cycles), then 1 seems as good a limit as any after all.
这是因为您正在使用“复制初始化”。如果你写的是
oB
: 的声明,它应该可以工作。两个初始化的语义是
不同的。对于
B oB(a)
,编译器尝试查找构造函数可以使用给定的参数调用它。在本例中,
B::B(A*)
可以调用,因为存在来自
Wrap
的隐式转换到
A*
。对于B oB = a
,语义是将a
隐式转换为类型
B
,然后使用B
的复制构造函数初始化oB
。 (这实际的副本可以优化掉,但是程序的合法性是
确定好像不是。)并且没有隐式转换
Wrap
到
B
,仅限Wrap
到
A*
。当然,赋值是有效的,因为赋值运算符还
采用
A*
,因此隐式转换开始发挥作用。It's because you're using "copy initialization". If you write the
declaration of
oB
:, it should work. The semantics of the two initializations are
different. For
B oB(a)
, the compiler tries to find a constructorwhich can be called with the given arguments. In this case,
B::B(A*)
can be called, because there is an implicite conversion from
Wrap<A>
to
A*
. ForB oB = a
, the semantics are to implicitly converta
totype
B
, then use the copy constructor ofB
to initializeoB
. (Theactual copy can be optimized out, but the legality of the program is
determined as if it weren't.) And there is no implicit conversion of
Wrap<A>
toB
, only ofWrap<A>
toA*
.The assignment works, of course, because the assignment operator also
takes a
A*
, and so the implicit conversion comes into play.该标准不允许链式隐式转换。如果允许,那么您可以编写这样的代码:
您不能期望
10
将转换为A
,然后再转换为B
然后转换为C
。同样的规则适用于隐式调用
运算符 T()
的隐式转换。考虑一下,
您不能期望
10
会转换为A
,然后再转换为B
(使用operator B()),然后转换为
C
。 S不允许此类链式隐式转换。你必须这样做:
The Standard doesn't allow chained implicit conversion. If it was allowed, then you could have written such code:
You cannot expect that
10
will convert toA
which then will convert toB
which then converts toC
.The same rule applies for implicit conversion that invokes
operator T()
implicitly.Consider this,
You cannot expect that
10
will convert toA
which then will convert toB
(usingoperator B()
) which then converts toC
. SSuch chained implicit conversions are not allowed. You've to do this:
这是因为 C++ 标准只允许一种用户定义的转换。根据§12.3/4:
作为解决方法,您可以使用显式形式调用构造函数:
This is because C++ Standard allows only one user-defined conversion. According to §12.3/4:
As a workaround you can use explicit form of calling the constructor: