我在使用auto和dynamic_cast时遇到了一个非常奇怪的行为。这是我的类层次结构:
class BaseInterface {
public:
virtual void someMethod()=0;
};
class Derived:public BaseInterface {
public:
virtual void someMethod1()=0;
void someMethod()override;
};
当然也有一些类实现了所有的派生方法。
然后是第三个类,它看起来像这样:
class ThirdClass {
public:
void demoMethod(BaseInterface&);
void anotherMethod(Derived&);
};
void ThirdClass::demoMethod(BaseInterface& obj) {
auto buffer=dynamic_cast<Derived&>(obj);
anotherMethod(buffer);
}
当我用gcc编译这个时,我得到一个"不能分配抽象类型的对象"错误。而当我替换
auto buffer=...
Derived& buffer=...
一切都编译得很好。为什么会这样呢?是auto没有推导出正确的类型还是怎么的?
我还发现了一个肮脏的伎俩,仍然使用auto:
void ThirdClass::demoMethod(Base& obj) {
auto buffer=dynamic_cast<Derived*>(&obj);
anotherMethod(*buffer);
}
你从auto
得到Derived
。用这个代替:
auto & buffer = dynamic_cast<Derived&>(obj);
§7.1.6.4/7:
因此,为了熟悉这个过程,看看用于推断当使用占位符类型声明的变量初始化时[…]推导出的返回类型或变量类型由类型确定它的初始化式。设
T
为变量的声明类型或者函数的返回类型。如果占位符是auto
类型说明符,则使用的规则确定推断的类型模板参数推导。[…]从T
中获取P
auto
出现在新创建的类型模板中参数U
[…]。使用模板规则推导U
的值从函数调用(14.8.2.1)中推导参数,其中P
是a函数模板形参类型和对应的实参是初始化器。
buffer
类型的实际规则:如果更改
会发生什么?template <typename U>
void f( U );
void f( Derived& );
调用f
的左值类型Derived
?显然,对于函数模板,U
将被推导为Derived
,这将导致推导失败。
这直接对应于你的例子中占位符类型的推导——auto
将被Derived
取代,这失败了,因为Derived
是抽象的。
一般来说,如果你写
auto obj = …;
obj
永远不会是引用,正如U
在调用上述函数模板时永远不会被推断为引用类型一样。
请使用auto&
:
auto& buffer = dynamic_cast<Derived&>(obj);
现在,P
是U&
:
template <typename U>
void f(U&);
当然, U
仍然被推断为Derived
,但P
的类型(实际上是buffer
的类型)是Derived&
。