如果编译器认为模板类的实例仍然是抽象的,即使派生的类实现了这种抽象方法,我想知道它是否是符合标准的行为。
我的编译器是Microsoft C / C ++编译器18.00.40629(Visual Studio 2013)
以下课程:
class MyClass {
virtual void Method() = 0;
};
template <typename T>
class MyClassT : public MyClass, public T {
};
class MyTemplateData {
void Method() {
}
};
MyClassT<MyTemplateData> MyInstance;
根据我的理解,类MyClassT<MyTemplateData>
是一个有效的实例,它实现了所有抽象方法,因为MyClassT派生自MyClass
和MyTemplateData
。为了能够通过保持期望的行为来编译这个类,我应该改变什么?
有一个解决方案,但让我解释为什么我不想跟随:
template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
T::Execute();
}
};
在上面的例子中,它确实会编译,但是这会假设我的T
类总是实现Method
。但是,我想要的是在Method
中有一个空的MyClassT
,而T::Method
如果T
类实现它,则应该被T::Method
覆盖。
所以下面也会编译,但template <typename T>
class MyClassT : public MyClass, public T {
void Method() {
}
};
MyClass* Interface = new MyClassT<MyTemplateData>();
Interface->Method(); // <- here, MyTemplateData::Method() gets never called
永远不会被调用。
struct InterfaceA {
virtual void MethodA() = 0;
};
struct InterfaceAB : InterfaceA {
virtual void MethodB() = 0;
};
class ImplA : public virtual InterfaceA {
public:
void MethodA() {
}
};
class ImplAB : public virtual InterfaceAB, public ImplA {
void MethodB() {
}
};
是否有解决方案,或者这是编译器特定的错误?
编辑:这是另一个例子,这次编译器应该确切地知道派生类实现了所需的抽象方法。
ImplAB
编译器期望类MethodA
实现InterfaceAB
,因为MethodA
要求实现MethodB
和ImplAB
。但是,由于ImplA
也派生自ImplA
并且编译器确切地知道class ImplABCD : public implements InterfaceABCD, public ImplA, public ImplB, public ImplC, public ImplC {
};
实现了该接口,所以应该没有任何问题。
至少应该有一个关键字给编译器提供一个提示,其中实现抽象方法,如下例所示。至少如果以任何方式使用VTABLE,我认为没有任何理由可以避免这种情况。
InterfaceABCD
此实现关键字表示ImplABCD
中的所有抽象方法都将在int currentPstn = lv_browseplans.getCurrentItem();
lv_browseplans.setAdapter(browsePlansAdapter);
lv_browseplans.setCurrentItem(currentPstn);
中实现,或者已在其中一个继承的类中实现。如果存在签名歧义,则处理大小写,因为当从具有相同基类的多个类继承时,c ++规范已经完成了这种情况。
答案 0 :(得分:3)
如果您不想要@ForEveR解决方案,那么您可以执行以下操作。
使用SFINAE std :: enable_if。 有些东西:
// Example program
#include <iostream>
#include <string>
struct MyClass
{
virtual void Method() = 0;
};
template< typename C, typename = void >
struct has_func_method
: std::false_type
{};
template< typename C>
struct has_func_method< C, typename std::enable_if<
std::is_same<
decltype( std::declval<C>().Method()),
void
>::value
>::type >
: std::true_type
{};
template <typename T, bool has_method = has_func_method<T>::value>
struct MyClassT : public MyClass, public T
{
};
template <typename T>
struct MyClassT<T, false> : public MyClass, public T
{
void Method()
{
//do nothing
std::cout << "doing nothing" << std::endl;
}
};
template <typename T>
struct MyClassT<T, true> : public MyClass, public T
{
void Method()
{
std::cout << "calling T::Method" << std::endl;
T::Method();
}
};
struct DataWithoutMethod
{
};
struct DataWithMethod
{
void Method()
{
std::cout << "method called" << std::endl;
}
};
int main()
{
MyClassT<DataWithoutMethod> data;
data.Method();
MyClassT<DataWithMethod> data2;
data2.Method();
}
编辑: 你可以在课堂上使用SFINAE而不是内部。 这样可能更好。 这是一个有效的例子:
{{1}}
答案 1 :(得分:1)
我想知道它是否符合标准行为
是
如果编译器认为模板类的实例仍然是抽象的
是。
即使派生的类实现了这种抽象方法。
它没有。
你没有压倒MyClass::Method
; T::Method
无关。
让我们暂时摆脱模板,因为它们无关紧要并且分散注意力:
class MyClass {
virtual void Method() = 0;
};
class MyTemplateData {
void Method() {
}
};
class MyClassT : public MyClass, public MyTemplateData {
};
这是继承树:
MyClass MyTemplateData
\ /
\ /
MyClassT
Method
必须在左侧实施;也就是说,在MyClass
,MyClassT
,或之间。右侧是不相关的继承分支:当您创建MyTemplateData::Method
时,这不会覆盖MyClass::Method
,因为MyTemplateData
和MyClass
之间没有继承关系。
覆盖仅适用于直线。