为什么没有为给定的转换操作符调用构造函数?

时间:2011-05-25 06:10:07

标签: c++ constructor compiler-errors typecast-operator

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后,为B::B(A*)调用了为什么Wrap<T>::operator T ()? [注意:在下一个语句中为B::operator = (A*)调用Wrap<T>::operator T ()

4 个答案:

答案 0 :(得分:10)

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

B ob = a;

意味着两次用户转换:

  • on aWrap<A>::operator A*()应该被称为
  • 结果:B::B(A*)应该被称为

@James Kanze的解释:这种语法称为“复制初始化”,实际上相当于B ob = B(a)(大部分时间都删除了副本)。这与B ob(a)不同,B ob = B(a); 是“直接初始化”并且可以有效。

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

ob = a;

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

ob.operator=(a);

是简称:

{{1}}

因此只需要一个用户定义的转换,这是允许的。

修改

由于评论中需要(对基里尔的回答),我们可以猜测一下动机。

链式转换可能很长很长,因此:

  • 可能会让用户感到惊讶 - 隐式转换可能已经令人惊讶,因为它......
  • 可能导致指数搜索可能性(对于编译器) - 它需要从两端开始,尝试检查所有可能的转换,并以某种方式“加入”两者(尽可能使用最短路径)。

此外,只要转换次数超过1次,就会遇到必须检测到周期的风险(即使可能不需要诊断,并且要遵守实施质量)。< / p>

所以,因为有必要限制以避免无限长的搜索(它可能未被指定,需要最低限度),并且由于超过1我们可能有新的问题(周期),那么1似乎是一个很好的限制毕竟。

答案 1 :(得分:7)

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

B oB(a);

,它应该工作。两个初始化的语义是 不同。对于B oB(a),编译器尝试查找构造函数 可以使用给定的参数调用。在这种情况下,B::B(A*) 可以被调用,因为有Wrap<A>的隐含转换 到A*。对于B oB = a,语义是隐式地将a转换为 键入B,然后使用B的复制构造函数初始化oB。 (该 实际的副本可以优化出来,但程序的合法性是 确定它好像不是。)并且没有隐含的转换 Wrap<A>B,仅Wrap<A>A*

当然,赋值也适用,因为赋值运算符也是如此 采用A*,因此隐式转换发挥作用。

答案 2 :(得分:5)

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

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

同样的规则适用于隐式调用operator 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。小号

不允许使用此类链式隐式转换。你要做到这一点:

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

答案 3 :(得分:2)

这是因为C ++ Standard只允许一个用户定义的转换。根据§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