C ++转换运算符重载和多态

时间:2013-01-12 22:40:59

标签: c++ casting polymorphism overloading operator-keyword

我对C ++的这种行为感到困惑:

struct A {
   virtual void print() const { printf("a\n"); }
};

struct B : public A {
   virtual void print() const { printf("b\n"); }
};

struct C {
   operator B() { return B(); }
};

void print(const A& a) {
   a.print();
}

int main() {
   C c;
   print(c);
}

所以,测验是,程序的输出是什么 - a或b?嗯,答案是答案。但为什么呢?

1 个答案:

答案 0 :(得分:10)

这里的问题是C ++ 03标准中的错误/错误/漏洞,不同的编译器试图以不同的方式修补问题。 (C ++ 11标准中不再存在此问题。)

两个标准的第8.5.3 / 5节都规定了如何初始化参考。这是C ++ 03版本(列表编号是我的):

  

类型cv1 T1的引用由cv2 T2类型的表达式初始化,如下所示:

     
      
  1. 如果是初始化表达式

         
        
    1. 是左值(但不是位字段),“cv1 T1”与“cv2 T2”或
    2. 引用兼容   
    3. 具有类类型(即T2是类类型),可以隐式转换为cv3 T3类型的左值,其中cv1 T1与{{1}引用兼容}}
    4.         

      然后引用在第一种情况下直接绑定到初始化表达式lvalue,并且引用在第二种情况下绑定到转换的左值结果。

        
    5. 否则,引用应为非易失性const类型(即cv3 T3应为cv1)。

    6.   
    7. 如果初始化表达式是右值,const类类型,T2cv1 T1引用兼容,则引用绑定在以下之一方式(选择是实现定义的):

           
          
      1. 引用绑定到rvalue(参见3.10)表示的对象或该对象中的子对象。
      2.   
      3. 创建类型cv2 T2 [sic]的临时表,并调用构造函数将整个右值对象复制到临时对象中。引用绑定到临时或临时的子对象。
      4.         

        无论副本是否真正完成,用于制作副本的构造函数都应该是可调用的。

          
      5. 否则,将使用非引用副本初始化(8.5)的规则从初始化表达式创建并初始化类型cv1 T2的临时表。然后将引用绑定到临时。

      6.   

手头的问题涉及三种类型:

  • 要创建的引用的类型。标准(两个版本)将此类型表示为cv1 T1。在这种情况下,它是T1
  • 初始化表达式的类型。标准将此类型表示为struct A。在这种情况下,初始化表达式是变量T2,因此cT2。请注意,由于struct Cstruct A不是参考兼容,因此无法直接将引用绑定到struct C。需要一个中间体。
  • 中间体的类型。标准将此类型表示为c。在这种情况下,这是T3。请注意,将转化运算符struct B应用于C::operator B()会将左值c转换为右值。

我标记为1.1和3的初始化已经完成,因为cstruct A不是参考兼容的。需要使用转换运算符struct C。 1.2是因为这个转换运算符返回一个rvalue,这规则为1.2。剩下的就是选项4,创建一个C::operator B()类型的临时表。严格遵守2003版本的标准迫使为此问题创建两个临时工,即使只有一个就足够了。

2011版标准通过将选项3替换为

来解决问题
  
      
  • 如果是初始化表达式

         
        
    • 是xvalue,类prvalue,数组prvalue或函数lvalue,cv1 T1是引用 -   与cv1 T1
    • 兼容   
    • 具有类类型(即,cv2 T2是类类型),其中T2T1不是引用相关的,并且可以隐式转换为xvalue,类prvalue ,或函数左值为T2,其中cv3 T3cv1 T1引用兼容,
    •   
         

    然后引用绑定到第一种情况下的初始化表达式的值,并绑定到第二种情况下的转换结果(或者,在任何一种情况下,绑定到适当的基类子对象)。在第二种情况下,如果引用是右值引用,并且用户定义的转换序列的第二个标准转换序列包括左值到右值转换,则程序格式不正确。

  •   

gcc系列编译器似乎选择严格遵守意图(避免创建不必要的临时),而打印“b”的其他编译器选择了标准的意图/更正。选择严格的合规并不一定值得称道;在2003版本的标准中存在其他错误/错误(例如,cv3 T3),其中gcc家族选择了严格遵守的理智。