【问题标题】:Why constructor is not called for given casting operator?为什么不为给定的强制转换运算符调用构造函数?
【发布时间】:2011-05-25 06:10:06
【问题描述】:
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&lt;T&gt;::operator T () 调用? [注意:B::operator = (A*) 在下一条语句中为Wrap&lt;T&gt;::operator T () 调用]

【问题讨论】:

  • 哪个编译器?在 VS2010 上编译良好。
  • @Agnel Kurian,那是因为 VS2010 部分支持 C++0x。
  • @Kirill:那么,以上在 C++0x 中是否有效?你是这个意思吗?
  • @Agnel Kurian,我刚刚看过 C++0x,不,它是无效的。似乎是VS2010中的扩展或错误。
  • 使用 gcc 4.4.3 我无法编译 B oB;,我必须使用 B oB(a);。所以我不清楚main 的第三行中的// ok 指的是什么。

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


【解决方案1】:

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

B ob = a;

意味着两次用户转化:

  • a: Wrap&lt;A&gt;::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 我们可能会遇到新问题(循环),因此 1 似乎与限制一样好毕竟任何。

【讨论】:

  • +1 正确答案。该标准不允许链式转换。它允许在表达式中进行一次用户转换。
  • 这个答案只是部分正确。这里的重点是“复制初始化”的语义,而不是“直接初始化”。
【解决方案2】:

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

B oB(a);

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

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

【讨论】:

  • +1 好点。我有一种感觉,B oB = a;B oB(a) 是一样的!
  • +1000。好的。我不知道这两种初始化的语义有什么区别。
【解决方案3】:

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

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。 S

这种链式隐式转换是不允许的。你必须这样做:

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

【讨论】:

    【解决方案4】:

    这是因为 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
    

    【讨论】:

    • 你能详细一点吗?尤其是不允许这样做的动机是什么。
    猜你喜欢
    • 2021-12-04
    • 2012-06-28
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2019-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多