为什么给定的转换运算符不调用构造函数?

发布于 2024-11-09 10:31:05 字数 531 浏览 0 评论 0原文

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::operator T () 调用 B::B(A*) ? [注意:在下一个语句中为 Wrap::operator T () 调用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鲸落 2024-11-16 10:31:05

问题是隐式调用的用户定义转换的数量受到标准的限制(为 1)。

B ob = a;

意味着两次用户转换:

  • 调用 Wrap::operator A*()
  • a 上:应在结果上 :B::B(A* ) 应该被称为

@James Kanze 的解释: 这种语法称为“复制初始化”,实际上相当于 B ob = B(a) (复制大部分时间都被忽略了)。这与 B ob(a) 不同,后者是“直接初始化”并且可以工作。

如果您明确限定其中任何一个,它将起作用,例如:

B ob = B(a);

另一方面,对于第二种情况没有问题:

ob = a;

是简写:

ob.operator=(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.

B ob = a;

implies two user conversions:

  • on a: Wrap<A>::operator A*() should be called
  • on the result: B::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 from B ob(a) which is a "direct initialization" and would have worked.

if you explicitly qualify any of this, it will work, for example:

B ob = B(a);

On the other hand, for the second case there is no issue:

ob = a;

is short-hand for:

ob.operator=(a);

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:

  • could surprise users -- implicit conversions may already be surprising as it is...
  • could lead to an exponential search of the possibilities (for the compiler) -- it would need to go from both ends, trying to check all possible conversions, and somehow "join" the two (with the shortest path possible).

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.

明月夜 2024-11-16 10:31:05

这是因为您正在使用“复制初始化”。如果你写的是
oB: 的声明

B oB(a);

,它应该可以工作。两个初始化的语义是
不同的。对于 B oB(a),编译器尝试查找构造函数
可以使用给定的参数调用它。在本例中,B::B(A*)
可以调用,因为存在来自 Wrap 的隐式转换
A*。对于 B oB = a,语义是将 a 隐式转换为
类型 B,然后使用 B 的复制构造函数初始化 oB。 (这
实际的副本可以优化掉,但是程序的合法性是
确定好像不是。)并且没有隐式转换
Wrap
B,仅限 WrapA*

当然,赋值是有效的,因为赋值运算符还
采用 A*,因此隐式转换开始发挥作用。

It's because you're using "copy initialization". If you write the
declaration of oB:

B oB(a);

, it should work. The semantics of the two initializations are
different. For B oB(a), the compiler tries to find a constructor
which 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*. For B oB = a, the semantics are to implicitly convert a to
type B, then use the copy constructor of B to initialize oB. (The
actual 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> to B, only of Wrap<A> to A*.

The assignment works, of course, because the assignment operator also
takes a A*, and so the implicit conversion comes into play.

可是我不能没有你 2024-11-16 10:31:05

该标准不允许链式隐式转换。如果允许,那么您可以编写这样的代码:

struct A
{
   A(int i) //enable implicit conversion from int to A
};  
struct B
{
   B(const A & a); //enable implicit conversion from A to B
};
struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 
C c = 10; //error

您不能期望 10 将转换为 A,然后再转换为 B然后转换为C


B b = 10; //error for same reason!

A a = 10;        //okay, no chained implicit conversion!
B ba = A(10);    //okay, no chained  implicit conversion!
C cb = B(A(10)); //okay, no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion

同样的规则适用于隐式调用运算符 T()隐式转换。

考虑一下,

struct B {};

struct A 
{
   A(int i); //enable implicit conversion from int to A
   operator B(); //enable implicit conversion from B to A
};

struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 

C c = 10; //error

您不能期望 10 会转换为 A,然后再转换为 B(使用 operator B()),然后转换为 C。 S

不允许此类链式隐式转换。你必须这样做:

C cb = B(A(10);  //okay. no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion

The Standard doesn't allow chained implicit conversion. If it was allowed, then you could have written such code:

struct A
{
   A(int i) //enable implicit conversion from int to A
};  
struct B
{
   B(const A & a); //enable implicit conversion from A to B
};
struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 
C c = 10; //error

You cannot expect that 10 will convert to A which then will convert to B which then converts to C.


B b = 10; //error for same reason!

A a = 10;        //okay, no chained implicit conversion!
B ba = A(10);    //okay, no chained  implicit conversion!
C cb = B(A(10)); //okay, no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion

The same rule applies for implicit conversion that invokes operator T() implicitly.

Consider this,

struct B {};

struct A 
{
   A(int i); //enable implicit conversion from int to A
   operator B(); //enable implicit conversion from B to A
};

struct C
{
   C(const B & b); //enable implicit conversion from B to C
}; 

C c = 10; //error

You cannot expect that 10 will convert to A which then will convert to B(using operator B()) which then converts to C. S

Such chained implicit conversions are not allowed. You've to do this:

C cb = B(A(10);  //okay. no chained implicit conversion!
C ca = A(10);    //error, requires chained implicit conversion
池予 2024-11-16 10:31:05

这是因为 C++ 标准只允许一种用户定义的转换。根据§12.3/4:

至多一个用户定义的转换(构造函数或转换函数)隐式应用于单个
值。

B oB = a; // not tried: ob( a.operator T*() ), 1 conversion func+1 constructor
oB = a;   // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=

作为解决方法,您可以使用显式形式调用构造函数:

B oB( a ); // this requires only one implicit user-defined conversion

This is because C++ Standard allows only one user-defined conversion. According to §12.3/4:

At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single
value.

B oB = a; // not tried: ob( a.operator T*() ), 1 conversion func+1 constructor
oB = a;   // OK: oB.operator=( a.operator T*() ), 1 conversion func+1 operator=

As a workaround you can use explicit form of calling the constructor:

B oB( a ); // this requires only one implicit user-defined conversion
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文