可能更好的是:为什么标准要求在这些情况下转发到基类? (是的,是的,为什么? - 因为。)
class B1 {
public:
virtual void f()=0;
};
class B2 {
public:
virtual void f(){}
};
class D : public B1,public B2{
};
class D2 : public B1,public B2{
public:
using B2::f;
};
class D3 : public B1,public B2{
public:
void f(){
B2::f();
}
};
D d;
D2 d2;
D3 d3;
MS给出:
sourceFile.cpp
sourceFile.cpp(24) : error C2259: 'D' : cannot instantiate abstract class
due to following members:
'void B1::f(void)' : is abstract
sourceFile.cpp(6) : see declaration of 'B1::f'
sourceFile.cpp(25) : error C2259: 'D2' : cannot instantiate abstract class
due to following members:
'void B1::f(void)' : is abstract
sourceFile.cpp(6) : see declaration of 'B
,类似于MS编译器。
我可能购买第一个案例,D。但是在D2-f中,使用声明定义了明确,为什么编译器不足以填写vtable?
标准中的哪种情况定义了这种情况?
添加回复
关于我接受的以下答案:
为什么这在规范中似乎不是错误? - 如果一个具有一系列非虚拟f()的继承层次结构,则派生类中的使用由使用语句确定,并且一个将基类中的f的decl更改为virtual然后可以更改哪个f在派生类中调用,使用using语句来选择它们的f。这是一个我不知道的c ++“陷阱”。它可能是语言的一部分,但这种“远距离行动”让我感到不安,对我来说似乎违反了某种正确/维护原则(我现在还不能完全表达)。
但我可以举个例子:
#include <iostream>
using std::cout;
namespace NonVirtual_f{
class C0 {
public:
void f(){cout<<"C0::f()"<<'\n';}
};
class C1 : public C0{
public:
void f(){cout<<"C1::f()"<<'\n';}
};
class C2 : public virtual C1{
public:
void f(){cout<<"C2::f()"<<'\n';}
};
class D3 : public virtual C1, public C2{
public:
using C1::f;
};
}//namespace NonVirtual_f
namespace Virtual_f{
class C0 {
public:
virtual void f(){cout<<"C0::f()"<<'\n';}
};
class C1 : public C0{
public:
void f(){cout<<"C1::f()"<<'\n';}
};
class C2 : public virtual C1{
public:
void f(){cout<<"C2::f()"<<'\n';}
};
class D3 : public virtual C1, public C2{
public:
using C1::f;
};
}//namespace Virtual_f
int main(int argc,const char* const*argv){
NonVirtual_f::D3 nv3;
nv3.f();
Virtual_f::D3 v3;
v3.f();
return 0;
}
输出:
C1::f()
C2::f()
所有改变的是C0中f的虚拟性。特别是一旦选择了基类中f的非虚拟性,如果某些派生类(通常一个 不能 知道),就无法在没有维护问题的情况下进行更改在上面的例子中已经“覆盖”了。
如果您反击“嗯,不要在非虚拟案例中覆盖那种方式”,我同意这是不好的做法,但这似乎不仅仅是这样。对我来说,语言应该:
不允许在NonVirtual :: D3中使用(目前不可能,因为可能会有其他重载的f引入[除非在功能案例中使用允许的签名])
或
完全禁止使用函数语句并强制转发
或
在所有情况下都使用了实际覆盖
或
允许函数的一些语法声明(基本上是一个函数使用),如:
void f(*signature*) = C2::f;
我到底错过了什么? 有人能想出一个方案来澄清标准中这个选择的“原因”吗?
答案 0 :(得分:3)
C ++标准在§10.3/ 2中说:
成员查找规则(10.2)用于确定派生类范围内虚函数的最终覆盖,但忽略 using-declarations 引入的名称。
因此,即使您使用using B2::f;
将B2::f()
带入派生类,也不会将其视为覆盖B1::f()
。
因此,D2
是抽象的,因为§10.4/ 4:
如果一个类包含或继承了至少一个纯虚函数,那么该类是抽象的,最终的覆盖是纯虚函数。