(GCC 错误?)隐式转换为派生类 [英] (GCC bug?) Implicit conversion to a derived class

查看:33
本文介绍了(GCC 错误?)隐式转换为派生类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've encountered a problem with implicit conversion in C++. The following is a minimal example:

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' 的对象.

What I would like the compiler to do is: convert b to a variable of type Ad (using the conversion defined in B) and pass the result to test. However, the above code does not compile in GCC (with C++11 enabled), the result being Cannot allocate an object of abstract type 'A'.

注意事项:

  1. Clang 编译这个.
  2. 如果您通过将 f()=0; 更改为 f() {} 使 A 成为非抽象的,则代码工作正常.
  3. 编译器确实找到了转换运算符(如 2 所示),但它没有做我希望它做的事情.
  1. Clang compiles this.
  2. If you make A non-abstract by changing f()=0; to f() {}, the code works just fine.
  3. The compiler does find the conversion operator (as indicated by 2), but it doesn't do what I'd like it to do.

推荐答案

(全部引自 N4140,C++14 FD)

(All quotes from N4140, the C++14 FD)

TL;DR:代码格式良好,这是(或曾经是)GCC 错误.

[dcl.init.ref]/5 中介绍了引用初始化的规则.我将首先向您展示没有覆盖它的项目符号 - 如果您想跳过它,请直接跳到第三个引用.

The rules for reference initialization are covered in [dcl.init.ref]/5. I'll first show you the bullet that doesn't cover it - if you want to skip that go straight to the third quote.

否则,引用应该是一个左值引用非易失性 const 类型(即,cv1 应为 const),或引用应为右值引用.

Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

  • 如果初始化表达式
    • 是 xvalue(但不是位域)、类纯右值、数组纯右值或函数左值,并且cv1 T1"与>cv2 T2",或
    • 有一个类类型(即T2是一个类类型),其中T1T2没有引用相关,并且可以转换为一个xvalue,类纯右值,或类型为cv3 T3"的函数左值,其中cv1 T1" 与cv3 T3"(见 13.3.1.6) 与引用兼容,
    • If the initializer expression
      • is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
      • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an xvalue, class prvalue, or function lvalue of type "cv3 T3", where "cv1 T1" is reference-compatible with "cv3 T3" (see 13.3.1.6),

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

      then the reference is bound to the value of the initializer expression in the first case and to the result of the conversion in the second case (or, in either case, to an appropriate base class subobject).

      并且参考兼容性在 [dcl.init.ref]/41 中定义.
      现在考虑链接的 13.3.1.6:

      And reference-compability is defined in [dcl.init.ref]/41.
      Now consider the linked 13.3.1.6:

      在8.5.3规定的条件下,可以绑定一个引用直接到作为应用结果的泛左值或类纯右值到初始化表达式的转换函数.超载分辨率用于选择要调用的转换函数.假设cv1 T"是引用的底层类型正在初始化,cv S"是初始化器的类型表达式,以 S 为类类型,候选函数为选择如下:

      Under the conditions specified in 8.5.3, a reference can be bound directly to a glvalue or class prvalue that is the result of applying a conversion function to an initializer expression. Overload resolution is used to select the conversion function to be invoked. Assuming that "cv1 T" is the underlying type of the reference being initialized, and "cv S" is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

      • 考虑了S及其基类的转换函数.那些非显式转换函数隐藏在 S 和 yield 类型lvalue reference to cv2 T2"(初始化左值引用时或右值引用时函数)或cv2 T2" [..],其中cv1 T"与cv2 T2"引用兼容(8.5.3),是候选函数.对于直接初始化,[..].
      • The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type "lvalue reference to cv2 T2" (when initializing an lvalue reference or an rvalue reference to function) or "cv2 T2" [..], where "cv1 T" is reference-compatible (8.5.3) with "cv2 T2", are candidate functions. For direct-initialization, [..].

      如您所见,您的转换函数不是本段之后的候选函数.因此 [dcl.init]/5 中的下一个项目符号适用:

      As you can see, your conversion function isn't a candidate after this paragraph. Thus the next bullet in [dcl.init]/5 is applicable:

      否则:

      • 如果 T1 是类类型,则使用cv1 类型的对象的复制初始化规则考虑用户定义的转换T1" 由用户自定义转换 (8.5, 13.3.1.4);该程序是如果相应的非引用复制初始化,则格式错误将是畸形的.调用转换的结果函数,如非引用复制初始化所述,是然后用于直接初始化引用.该程序是如果直接初始化不会导致直接绑定或者是否涉及用户定义的转换.
      • If T1 is a class type, user-defined conversions are considered using the rules for copy-initialization of an object of type "cv1 T1" by user-defined conversion (8.5, 13.3.1.4); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. The program is ill-formed if the direct-initialization does not result in a direct binding or if it involves a user-defined conversion.

      注意短语程序是如果相应的非引用复制初始化,则格式错误将是不正确的"可能意味着

      B b;
      A a = b;
      

      格式错误,程序格式错误.我认为这是措辞的缺陷或含糊不清,而不是 GCC 不接受代码的原因.当然,措辞仅针对初始化本身,而不是首先可以创建类型为 T1(又名 A)的大多数派生对象.
      最后13.3.1.4接受我们的转换函数:

      is ill-formed, the program is ill-formed. I believe this to be a defect or vagueness in the wording though, and not the reason that GCC does not accept the code. Assuredly the wording solely aims at the initialization itself, not the fact that a most-derived object of type T1 (aka A) can be created in the first place.
      Finally 13.3.1.4 accepts our conversion function:

      假设cv1 T"是被初始化对象的类型,以T为类类型,候选函数被选为如下:

      Assuming that "cv1 T" is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:

      • T 的转换构造函数 (12.3.1) 是候选函数.
      • 当初始化表达式的类型为类类型cv S"时,S的非显式转换函数及其根据类被考虑.[..].那些没有隐藏在S中的并产生一个类型,其 cv-unqualified versionT 是其派生类是候选函数.
      • The converting constructors (12.3.1) of T are candidate functions.
      • When the type of the initializer expression is a class type "cv S", the non-explicit conversion functions of S and its base classes are considered. [..]. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions.

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

      Now the last question is whether in

      A const& ref(Ad());
      

      ref 是直接绑定的.确实如此.
      因此原始参数引用直接绑定,不必创建 A 类型的大多数派生对象.
      GCC 可能认为必须初始化类型为 A 的临时文件,并将引用绑定到该临时文件.或者迂腐地遵循上述有缺陷的措辞,这种可能性很小.

      ref is bound directly. And it is.
      Thus the original parameter reference binds directly, and no most-derived object of type A must be created.
      What GCC presumably thinks is that a temporary of type A must be initialized and the reference be bound to that temporary. Or it pedantically follows the above defected wording, which is very unlikely.

      1)

      给定类型cv1 T1"和cv2 T2"、cv1"em> T1" 与引用相关cv2 T2" 如果 T1T2 的类型相同,T1T2 的基类.如果 T1cv1 T1" 与cv2 T2" 引用兼容是与 T2cv1 相关的引用与 或比 cv2 更高的 cv 资格.

      Given types "cv1 T1" and "cv2 T2," "cv1 T1" is reference-related to "cv2 T2" if T1 is the same type as T2, or T1 is a base class of T2. "cv1 T1" is reference-compatible with "cv2 T2" if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2.

      这篇关于(GCC 错误?)隐式转换为派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆