Как проверить параметры шаблона во время компиляции, когда шаблонный класс не содержит используемых функций-членов? - PullRequest
6 голосов
/ 07 октября 2010

У меня есть следующее шаблонное struct:

template<int Degree>
struct CPowerOfTen {
enum { Value = 10 * CPowerOfTen<Degree - 1>::Value };
};

template<>
struct CPowerOfTen<0> {
    enum { Value = 1 };
};

, которое будет использоваться следующим образом:

const int NumberOfDecimalDigits = 5;
const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1;
// now can use both constants safely - they're surely in sync

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

Как мне это сделать?Я попытался добавить деструктор к CPowerOfTen:

~CPowerOfTen() {
    compileTimeAssert( Degree >= 0 );
 }

, но поскольку он не вызывается напрямую, Visual C ++ 9 решает не создавать его экземпляр, и поэтому оператор assert во время компиляции вообще не оценивается.

Как я могу применить проверку во время компиляции, чтобы Degree был неотрицательным?

Ответы [ 5 ]

8 голосов
/ 07 октября 2010
template<bool> struct StaticCheck;
template<> struct StaticCheck<true> {};

template<int Degree> 
struct CPowerOfTen : StaticCheck<(Degree > 0)> { 
    enum { Value = 10 * CPowerOfTen<Degree - 1>::Value }; 
}; 

template<> 
struct CPowerOfTen<0> { 
    enum { Value = 1 }; 
}; 

Редактировать: без бесконечной рекурсии.

// Help struct
template<bool, int> struct CPowerOfTenHelp;

// positive case    
template<int Degree> 
struct CPowerOfTenHelp<true, Degree> { 
    enum { Value = 10 * CPowerOfTenHelp<true, Degree - 1>::Value }; 
}; 

template<> 
struct CPowerOfTenHelp<true, 0> { 
    enum { Value = 1 }; 
}; 

// negative case
template<int Degree> 
struct CPowerOfTenHelp<false, Degree> {}

// Main struct
template<int Degree> 
struct CPowerOfTen : CPowerOfTenHelp<(Degree >= 0), Degree> {};
5 голосов
/ 07 октября 2010

Вы можете использовать BOOST_STATIC_ASSERT макрос. Или реализуйте свой собственный, самый простой способ заставить сбой выполнить определение типа массива из N элементов, где N положительно / отрицательно в зависимости от аргумента.

Проблема с этим подходом состоит в том, что он выдаст ошибку, но тем не менее попытается выполнить рекурсию. Взгляните на boost::enable_if_c, чтобы использовать SFINAE, чтобы не создавать экземпляр шаблона, если аргумент отрицательный.

5 голосов
/ 07 октября 2010

Вы можете использовать uint.Вы не получите ошибку времени компиляции, но, по крайней мере, это будет самодокументирование.

2 голосов
/ 07 октября 2010

Вы можете переслать реализацию в класс, также приняв параметр bool, указывающий, можно ли вычислить результат.

#include <limits>
template <int Degree, bool InRange>
struct PowerOfTenImpl
{
    enum {Value = 10 * PowerOfTenImpl<Degree - 1, InRange>::Value};
};

template <>
struct PowerOfTenImpl<0, true>
{
    enum {Value = 1};
};

template <int Degree>
struct PowerOfTenImpl<Degree, false>
{
};

template<int Degree>
struct CPowerOfTen {
    enum { Value = PowerOfTenImpl<Degree, Degree >= 0 && 
      Degree <= std::numeric_limits<int>::digits10>::Value };
};

int main()
{
    const int a = CPowerOfTen<4>::Value;
    const int b = CPowerOfTen<1000>::Value;
    const int c = CPowerOfTen<-4>::Value;
}
1 голос
/ 07 октября 2010

Как насчет реализации макроса STATIC_CHECK?

template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true> {}; //specialized only for true

#define STATIC_CHECK(expr)  (CompileTimeError<(expr) != 0>())

Внутри main()

 const int NumberOfDecimalDigits = -1;
 STATIC_CHECK(NumberOfDecimalDigits > 0); // Error : invalid use of incomplete type struct CompileTimeError<false>

 const int MaxRepresentableValue = CPowerOfTen<NumberOfDecimalDigits>::Value - 1; 
...