如果调用未重写的虚方法,如何强制编译器错误?

时间:2014-07-07 08:57:45

标签: c++ templates inheritance compiler-errors pragma

在C ++中编写模板基类时,这是一个关于样式和安全性的相当普遍的问题。但是,请耐心等待,最后有一个具体的问题......

我有一个模板基类,当类型T是基本类型(int,float等)时,它完全实现了所需的功能。但是,如果T不是原始的(例如,如果T是需要使用一组特定参数调用的构造函数的类),则需要覆盖某些方法,因此它们被声明为虚拟模板。该模板还包含一个纯虚方法,强制它是抽象的,所以为了使用它必须派生自:基于基本类型的派生类可以使用提供的所有方法和只覆盖纯虚拟类,而基于非原始类型的派生类应覆盖所有虚方法。

例如:

template <typename T> class myTemplate
{
public:
    // Constructor:
    myTemplate<T>();

    // Destructor:
    virtual ~myTemplate();

    // General function, valid for all classes derived from this:
    void printMe()
    {
        printf("Hello, I'm from the template\r\n");
    }

    // Primitive function, must be overridden for non-primitive types:
    virtual T DoSomething(const T& myClass)
    {
        T result = result + static_cast<T>(1);
        return result;
    }

    // Primitive function, must be overridden for non-primitive types:
    virtual T DoSomethingElse(const T& myClass)
    {
        T result = result - static_cast<T>(1);
        return result;
    }

    // Pure Virtual function, must be overridden in all cases:
    virtual void RefrainFromDoingAnythingAtAll(const T& myClass) = 0
};


class myIntegerClass : public myTemplate<int>
{
public:
    virtual void RefrainFromDoingAnythingAtAll(const int& myInteger) {} 
};

假设现在我想要创建一个派生类,我希望调用它来“DoSomething”,但不能设想“DoSomethingElse”有用的任何情况。因此,我想重新实现'DoSomething',但不要打扰'DoSomethingElse'。然而,如果在某些时候派生类的'DoSomethingElse'方法调用(或者因为我真的打算称'DoSomething'但是错误地写了错误的东西,或者因为我出现了一个情况之前未能设想),然后我想要发出一个编译器警告,提醒我除非我首先重新实现'DoSomethingElse',否则我不能这样做。

例如:

class myWatermelonClass : public myTemplate<Watermelon>
{
public:
    virtual Watermelon DoSomething(const Watermelon &myWatermelon)
    {
          // Just return a copy of the original:
          Watermelon anotherWatermelon(myWatermelon);
          return anotherWatermelon;
    }

    virtual Watermelon DoSomethingElse(const Watermelon &myWatermelon)
    {
          // This routine shouldn't ever get called: if it does, then 
          // either I've made an error or, if I find that I really need
          // to reimplement it after all, then I'd better buckle down
          // and do it.
          < How do I make the compiler remind me of this? >
    }        

    virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {}   
};

显然我知道标准的#error和#warning编译器指令,但如果我使用其中一个,那么每次编译时都会标记错误(或警告)。我想要的是确保在编译时出现错误,如果我疏忽地调用

DoSomethingElse(aSpecificWatermelon);

来自我的代码中的某个地方,但不是。有没有办法实现这个目标?或者这首先是一个根本不好的设计?

2 个答案:

答案 0 :(得分:2)

我认为你滥用模板和虚拟调度组合。例如,您的基类不会为任何不支持加法运算符且可从int构造的类型进行编译。每次myTemplate隐式专门化,所有虚拟函数都将编译,即使你在派生类中重写它们:

virtual T DoSomething(const T& myClass)
{
    T result = result + static_cast<T>(1); // compile error when T == Watermelon
    return result;
}

在您的情况下,您要寻找的是显式模板专业化:

template <>
class myTemplate<Watermelon>
{
public:
    // General function, valid for all classes derived from this:
    void printMe()
    {
        printf("Hello, I'm from the Watermelon template specialisation\r\n");
    }

    virtual Watermelon DoSomething(const Watermelon &myWatermelon)
    {
        // Just return a copy of the original:
        Watermelon anotherWatermelon(myWatermelon);
        return anotherWatermelon;
    }

    // notice there's no DoSomethingElse!

    virtual void RefrainFromDoingAnythingAtAll(const Watermelon& myWatermelon) {}   
};

现在在DoSomethingElse的实例上调用myTemplate<Watermelon>会立即给出编译错误,因为没有这样的功能。

答案 1 :(得分:2)

通过使用static_assert,当您尝试调用不符合特定条件的函数时,可能会导致编译错误。根本不需要虚拟功能。这是一个例子:

#include <iostream>
#include <type_traits>

using std::cout;

template <typename T>
struct myTemplate {
    void printMe() { cout << "Hello, I'm from the template\r\n"; }

    T DoSomething(const T& myClass)
    {
        static_assert(
          std::is_fundamental<T>::value,
          "DoSomething must be redefined in derived classes "
          "for non-fundamental types."
        );
        T result = myClass + static_cast<T>(1);
        return result;
    }

    T DoSomethingElse(const T& myClass)
    {
        static_assert(
          std::is_fundamental<T>::value,
          "DoSomethingElse must be redefined in derived classes "
          "for non-fundamental types."
        );
        T result = myClass - static_cast<T>(1);
        return result;
    }

    template <typename U>
    struct never_true { static const bool value = false; };

    void RefrainFromDoingAnythingAtAll(const T&)
    {
        static_assert(
            never_true<T>::value,
            "RefrainFromDoingAnythingAtAll must be redefined "
            "in derived classes."
        );
    }
};

struct Watermelon {
};

struct Lemon {
};

struct myIntegerClass : myTemplate<int> {
  void RefrainFromDoingAnythingAtAll(const int &) { }
};

struct myWatermelonClass : myTemplate<Watermelon> {
    Watermelon DoSomething(const Watermelon&)
    {
      return Watermelon();
    }

    Watermelon DoSomethingElse(const Watermelon&)
    {
      return Watermelon();
    }

    void RefrainFromDoingAnythingAtAll(const Watermelon &) { }
};


struct myLemonClass : myTemplate<Lemon> {
};

int main()
{
    myIntegerClass x;
    x.DoSomething(5); // works
    x.DoSomethingElse(5); // works
    x.RefrainFromDoingAnythingAtAll(5); // works
    myWatermelonClass y;
    y.DoSomething(Watermelon()); // works
    y.DoSomethingElse(Watermelon()); // works
    y.RefrainFromDoingAnythingAtAll(Watermelon()); // works
    myLemonClass z;
    z.DoSomething(Lemon()); // error
    z.DoSomethingElse(Lemon()); // error
    z.RefrainFromDoingAnythingAtAll(Lemon()); // error
}