如何在尝试为某些非类型值实例化模板子类方法时产生编译器错误?

How do I produce a compiler error upon an attempt to instantiate a template subclass method for certain non-type values?

如果使用程序为某个模板参数调用非类型模板 class 方法,我想生成一个编译器错误。

typedef SubWithTemplate<1> SubWithTemplate1;
typedef SubWithTemplate<2> SubWithTemplate2;

SubWithTemplate1 &subWithTemplate1 = SubWithTemplate1::instance;
SubWithTemplate2 &subWithTemplate2 = SubWithTemplate2::instance;

subWithTemplate1.doSomething(); // Should compile OK
subWithTemplate1.doSomethingElse(); // Should compile OK
subWithTemplate2.doSomething(); // Should NOT compile OK
subWithTemplate2.doSomethingElse(); // Should compile OK

我的出发点是下面两个classes:

Super.h:

class Super {
    protected:
        Super() {}
    public:
        virtual void doSomething();
        void doSomethingElse();
};

Super.cpp:

void Super::doSomething() {}
void Super::doSomethingElse() {}

SubWithTemplate.h:

template<int SUBNUMBER>
class SubWithTemplate : public Super {

    public:
        static SubWithTemplate<SUBNUMBER> instance;
        void doSomething() {
            // Do something
        };

    private:
        SubWithTemplate() : Super() {}
};

template<int SUBNUMBER>
SubWithTemplate<SUBNUMBER> SubWithTemplate<SUBNUMBER>::instance;

我对 Boost 或 mpl 不是很流利,但我有一些模糊的感觉 BOOST_MPL_ASSERT 可以给我带来一些成功。但我无法理解其中的本质。

我试过类似的方法:

SubWithTemplate.h:

...
void doSomething() {
    BOOST_MPL_ASSERT_MSG(<some test on SUBNUMBER being different from 2 and 7 and less than 25>, <what here?>, <what here?> )
};
...

我不希望 Super 被模板化,因为它应该是所有子classes 的相同实例化。

如果我能避免在 doSomething 上使用 virtual,那就更好了。

如果有比我更专业的人能帮助我,我将不胜感激。

不是很好的解决方案,但是...如果您可以使用 C++11,通过 SFINAE 禁用 doSomething() 怎么样?

在下面的示例中,doSomething() 对除 2

之外的所有 SUBNUMBER 值启用
#include <type_traits>

class Super
 {
   protected:
      Super () {}
      void doSomething () {}

   public:
      void doSomethingElse () {}
 };

template <int SUBNUMBER>
class SubWithTemplate : public Super
 {
    public:
        static SubWithTemplate<SUBNUMBER> instance;

        template <int I = SUBNUMBER>
        typename std::enable_if<I!=2>::type doSomething ()
         { Super::doSomething(); }

    private:
        SubWithTemplate () : Super() {}
 };

template<int SUBNUMBER>
SubWithTemplate<SUBNUMBER> SubWithTemplate<SUBNUMBER>::instance;

typedef SubWithTemplate<1> SubWithTemplate1;
typedef SubWithTemplate<2> SubWithTemplate2;

int main()
 {
   SubWithTemplate1 &subWithTemplate1 = SubWithTemplate1::instance;
   SubWithTemplate2 &subWithTemplate2 = SubWithTemplate2::instance;

   subWithTemplate1.doSomething();     // OK
   subWithTemplate1.doSomethingElse(); // OK
   //subWithTemplate2.doSomething();   // compilation error
   subWithTemplate2.doSomethingElse(); // OK
 }

--- 编辑 ---

正如 Guillaume Racicot 所指出的(谢谢!)这个解决方案可以通过显式模板值来规避(I = SUBNUMBER 只是一个默认值)。

所以如果

subWithTemplate2.doSomething();

给出一个编译错误(按照OP的要求),

subWithTemplate2.doSomething<1>();

编译没有问题。

为避免这种情况,我可以提出几个解决方案。

(1) 您可以在函数体中添加一个 static_assert(),以强制执行 I == SUBNUMBER;像

    template <int I = SUBNUMBER>
    typename std::enable_if<I!=2>::type doSomething ()
     {
       static_assert(I == SUBNUMBER, "I != SUBNUMBER; this in wrong");
       Super::doSomething();
     }

(2) 按照 Guillaume Racicot 的建议(再次感谢!),您可以在 std::enable_if<> 测试中集成 I == SUBNUMBER;像

    template <int I = SUBNUMBER>
    typename std::enable_if<(I!=2) && (I == SUBNUMBER)>::type
       doSomething ()
     { Super::doSomething(); }

我发现第二种解决方案更优雅一些,但我不是专家,对我来说,这是一个品味问题。

--- 编辑 2 ---

how could I prevent the SubWithTemplate class from being instantiated unless the SUBNUMBER is within a given interval?

防爆满class?不仅是doSomething()方法?

我想到的第一个方法是使用 static_alert()

举例来说,如果你想只允许[5,10[(包括5,排除10)范围内的SUBNUMBERs,你可以这样写构造函数。

SubWithTemplate () : Super()
 { static_assert((SUBNUMBER >= 5) && (SUBNUMBER < 10), "error message"); }

但我想还有其他方法。

--- 编辑 3 ---

另一种防止 SubWithTemplate class 被实例化的方法,除非 SUBNUMBER 在给定的时间间隔内。

一种在 C++98 中也适用的方法。

它基于默认专业化和模板默认值。

class Super
 {
   protected:
      Super () {}
      void doSomething () {}

   public:
      void doSomethingElse () {}
 };

template<bool b> struct boolWrapper {};

template <int I, bool = (I >= 0) && (I <= 20)>
struct rangeLimit;

template <int I>
struct rangeLimit<I, true>
 { };

template <int SUBNUMBER>
class SubWithTemplate : public Super, public rangeLimit<SUBNUMBER>
 {
    public:
        static SubWithTemplate<SUBNUMBER> instance;

        void doSomething ()
         { Super::doSomething(); }

    private:
        SubWithTemplate () : Super() {}
 };

template<int SUBNUMBER>
SubWithTemplate<SUBNUMBER> SubWithTemplate<SUBNUMBER>::instance;

typedef SubWithTemplate<1> SubWithTemplate1;
typedef SubWithTemplate<2> SubWithTemplate2;
typedef SubWithTemplate<20> SubWithTemplate20;
//typedef SubWithTemplate<21> SubWithTemplate21; compilation error

int main()
 {
 }