我在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'的对象。
有些注意事项:
f()=0;
更改为f() {}
来使A非抽象,则代码可以正常运行。答案 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
是一个类类型),其中T1
与T2
没有引用相关,可以转换为一个x值, 类prvalue ,或类型为“ cv3T3
”的函数左值,其中“ cv1T1
”与 cv3T3
“参考兼容”(见13.3.1.6),然后 引用绑定到初始化表达式的值 第一种情况和到第二种情况下的转换结果(或者, 在任何一种情况下,到适当的基类子对象)。
参考兼容性在[dcl.init.ref] / 4 1 中定义。
现在考虑链接的13.3.1.6:
在8.5.3中指定的条件下,可以绑定引用 直接到glvalue或类prvalue,这是应用的结果 转换函数到初始化表达式。超载 resolution用于选择要调用的转换函数。 假设“ cv1
T
”是引用的基础类型 正在初始化,“ cvS
”是初始化程序的类型 表达式,S
类类型,候选函数是 选择如下:
- 考虑
S
及其基类的转换函数。那些非显式转换函数不是 隐藏在S
中并且收益类型“对 cv2T2
的左值引用” (初始化左值引用或右值引用时) 功能)或“ cv2T2
”[..], 其中“ cv1T
”与“ cv2T2
”是参考兼容的(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
”和“ cv2T2
,”“ cv1 {{1} }“与引用有关 “ cv2T1
”如果T2
与T1
的类型相同,则或T2
是T1
的基类} 即可。 “ cv1T2
”与“ cv2T1
”引用兼容,如果T2
是 与T1
和 cv1 相关的引用与cv资格相同,或 比 cv2 更多的cv资格。