即使模板类实现了抽象方法

时间:2015-10-08 10:25:13

标签: c++ templates inheritance override virtual-method

如果编译器认为模板类的实例仍然是抽象的,即使派生的类实现了这种抽象方法,我想知道它是否是符合标准的行为。

我的编译器是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派生自MyClassMyTemplateData。为了能够通过保持期望的行为来编译这个类,我应该改变什么?

有一个解决方案,但让我解释为什么我不想跟随:

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要求实现MethodBImplAB。但是,由于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 ++规范已经完成了这种情况。

2 个答案:

答案 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必须在左侧实施;也就是说,在MyClassMyClassT之间。右侧是不相关的继承分支:当您创建MyTemplateData::Method时,这不会覆盖MyClass::Method,因为MyTemplateDataMyClass之间没有继承关系。

覆盖仅适用于直线。