(GCC bug?)隐式转换为派生类

时间:2014-12-28 11:17:09

标签: c++ class gcc type-conversion abstract-class

我在C ++中遇到了隐式转换的问题。以下是一个最小的例子:

struct A {
  virtual void f()=0; // abstract
};

struct Ad : A {
  virtual void f() {} // not abstract
};

struct B {
  operator Ad () const { return Ad(); }
};

void test(A const &lhs) {}

int main()
{
  B b;
  test(b);
}

我希望编译器做的是:将b转换为Ad类型的变量(使用B中定义的转换)并将结果传递给 test 。但是,上面的代码不能在GCC中编译(启用C ++ 11),结果是无法分配抽象类型'A'的对象。

有些注意事项:

  1. Clang汇编了这个。
  2. 如果您通过将f()=0;更改为f() {}来使A非抽象,则代码可以正常运行。
  3. 编译器确实找到了转换运算符(如2所示),但它没有按照我的意愿去做。

1 个答案:

答案 0 :(得分:9)

(所有引自N4140,C ++ 14 FD)

TL; DR:代码格式正确,这是(或曾经是)GCC错误。

参考初始化的规则在[dcl.init.ref] / 5中有所介绍。我首先会告诉你没有覆盖它的子弹 - 如果你想跳过它直接进入第三个引用。

  

否则,引用应该是对a的左值引用   非易失性const类型(即 cv1 应为const),或者   引用应为右值引用。

     
      
  • 如果是初始化表达式   
        
    • 是xvalue(但不是位字段),类prvalue,数组prvalue或函数lvalue,“ cv1 T1”与 cv2 <引用兼容/ em> T2“,   或
    •   
    • 有一个类类型(即,T2是一个类类型),其中T1T2没有引用相关,可以转换为一个x值,   类prvalue ,或类型为“ cv3 T3”的函数左值,其中“ cv1 T1”与 cv3 T3“参考兼容”(见13.3.1.6)
    •   
  •   
     

然后   引用绑定到初始化表达式的值   第一种情况和到第二种情况下的转换结果(或者,   在任何一种情况下,到适当的基类子对象)。

参考兼容性在[dcl.init.ref] / 4 1 中定义。
现在考虑链接的13.3.1.6:

  

在8.5.3中指定的条件下,可以绑定引用   直接到glvalue或类prvalue,这是应用的结果   转换函数到初始化表达式。超载   resolution用于选择要调用的转换函数。   假设“ cv1 T”是引用的基础类型   正在初始化,“ cv S”是初始化程序的类型   表达式,S类类型,候选函数是   选择如下:

     
      
  • 考虑S及其基类的转换函数。那些非显式转换函数不是   隐藏在S 中并且收益类型“对 cv2 T2的左值引用”   (初始化左值引用或右值引用时)   功能)或“ cv2 T2”[..],   其中“ cv1 T”与“ cv2 T2”是参考兼容的(8.5.3),   是候选职能。对于直接初始化,[..]。
  •   

如您所见,您的转化功能在本段后面不是候选人。因此,[dcl.init] / 5中的下一个项目适用:

  

否则:

     
      
  • 如果T1是类类型,则使用“ cv1 类型的对象的复制初始化规则来考虑用户定义的转换   T1“按用户定义的转换(8.5,13.3.1.4);该计划是   如果相应的非参考拷贝初始化,则形成错误   会不合时宜的。 转化调用的结果   函数,如非参考拷贝初始化所述,是   然后用于直接初始化参考。该计划是   如果直接初始化不会导致直接的错误形成   绑定或是否涉及用户定义的转换。
  •   

请注意,该程序的短语是#34;  如果相应的非参考拷贝初始化,则形成错误  将是不正确的&#34;可能暗示作为

B b;
A a = b;

形象不对称,该计划格式不正确。我认为这是措辞中的缺陷或模糊,而不是GCC不接受代码的原因。确实,措辞仅针对初始化本身,而不是首先可以创建类型为T1(又名A)的最派生对象的事实。
最后13.3.1.4接受我们的转换函数:

  

假设“ cv1 T”是要初始化的对象的类型,   使用T类类型,候选函数被选为   如下:

     
      
  • T的转换构造函数(12.3.1)是候选函数。
  •   
  • 当初始化表达式的类型是类类型“ cv S”时,S及其基类的非显式转换函数   课程被考虑。 [..]。 S内未隐藏的内容   并且产生一个类型,其cv-nonqualified version 是与它相同的类型   T是其派生类,是候选函数。
  •   

现在最后一个问题是是否在

A const& ref(Ad());

ref直接绑定。它是。
因此,原始参数引用直接绑定,并且不必创建类型为A的最派生对象 GCC可能认为的是,必须初始化类型为A的临时值,并且该引用必须与该临时值绑定。或者它迂腐地遵循上述瑕疵措辞,这是不太可能的。


1)

  

指定类型“ cv1 T1”和“ cv2 T2,”“ cv1 {{1} }“与引用有关   “ cv2 T1”如果T2T1的类型相同,则T2T1的基类} 即可。   “ cv1 T2”与“ cv2 T1”引用兼容,如果T2是   与T1 cv1 相关的引用与cv资格相同,或   比 cv2 更多的cv资格。