2017-01-08 12 views
1

Я хотел бы сгенерировать ошибку компилятора, если используемая программа вызывает метод класса нестандартного шаблона для определенного аргумента шаблона.Как создать ошибку компилятора при попытке создать экземпляр метода подкласса шаблона для определенных значений не-типа?

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 

Моя точка является следующие два класса:

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?>) 
}; 
... 

Я не хочу Супер быть Шаблонными, как это должно быть одинаковой конкретизацией для всех подклассов.

Если бы я мог избежать использования виртуального на doSomething, еще лучше.

Я был бы очень благодарен, если бы кто-то из более чем меня-экспертов мог мне помочь.

+0

Предполагая, что вы можете достичь этого (в чем я сомневаюсь), но так как 'йоЗотеЬЫпд()' является виртуальным, как вы можете предотвратить его от вызова через указатель типа 'Супер *' на объект типа ' SubWithTemplate <2> '? Этот вызов обнаруживается только во время выполнения! –

+0

Я думаю, что проблема решена путем удаления виртуального и защиты метода. Но все же экземпляр SubWithTemplate <2> может вызывать его защищенный супер метод. В моем случае это приемлемо, потому что Super не предназначен для наследования пользователем библиотеки, отображается только SubWithTemplate. –

ответ

3

Не отличное решение, но ... если вы можете использовать C++ 11, как насчет отключения doSomething() через SFINAE?

В следующем примере в doSomething() включена для всех значений SUBNUMBER кроме 2

#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 
} 

--- EDIT ---

Как отметил Гийом Racicot (спасибо!) Это решение можно обойти, объясняя значение шаблона (I = SUBNUMBER является только по умолчанию).

Так что если

subWithTemplate2.doSomething(); 

дают ошибку компиляции (как на вопрос ОП),

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 (еще раз спасибо!), вы можете интегрировать I == SUBNUMBER в тест std::enable_if<>; что-то вроде

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

Я нашел второе решение чуть более элегантный, но я не эксперт, и для меня, это дело вкуса.

--- EDIT 2 ---

, как я мог бы предотвратить класс SubWithTemplate от того экземпляра, если SUBNUMBER не находится в пределах заданного интервала?

Горячий, чтобы предотвратить полный класс? Не только метод doSomething()?

Первый способ, который приходит мне в голову, - использование static_alert().

Например, если вы хотите разрешить только SUBNUMBER s в диапазоне [5,10 [(5 включенных, 10 исключенных), вы можете написать конструктор следующим образом.

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

Но, я полагаю, существуют и другие способы.

--- EDIT 3 ---

Другой способ предотвратить класс SubWithTemplate от того экземпляра, если 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() 
{ 
} 
+0

Рассмотрите возможность изменения условия в enable_if на 'I! = 2 && I == SUBNUMBER', чтобы защитить функцию от переопределения значения' I'. –

+0

Благодарим всех вас за это решение. Он отлично работает для меня, мои основные требования удовлетворены. Но если некоторые из вас найдут более красивый рецепт, вы очень желанны. @ max66, вы пишете «... Не отличное решение ...», о чем вы думаете, более конкретно? –

+0

@GuillaumeRacicot - вы правы; Благодарю. – max66