2010-10-07 7 views
6

Mam następujący matrycy struct:Jak sprawdzać poprawność parametrów szablonu w czasie kompilacji, gdy klasa szablonowa nie zawiera użytecznych funkcji składowych?

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

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

który ma być używany tak:

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

teraz, że szablon wymaga Degree być nieujemna. Chciałbym wymusić na tym etapie kompilację.

Jak to zrobić? Próbowałem dodać destruktora do CPowerOfTen:

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

ale ponieważ nie jest wywoływany bezpośrednio Visual C++ 9 nie zdecyduje się go instancji, a więc stwierdzenie assert kompilacji nie jest w ogóle oceniane.

Jak mogę wymusić sprawdzanie podczas kompilacji dla Degree nieuzytywne?

Odpowiedz

8
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 }; 
}; 

Edit: bez nieskończonej rekurencji.

// 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> {}; 
+0

Nie zatrzymanie rekurencji w czasie kompilacji jest jednak dużym problemem? – visitor

5

Możesz użyć uint. Nie otrzymasz błędu podczas kompilacji, ale przynajmniej będzie to samo-dokumentowanie.

+0

Dokładnie. Liczby nieujemne to tylko jedna porażka. A co z CPowerOfTen <10000>? To i tak nie powinno działać. – harper

+1

NIGDY nie używaj funkcji uint do wymuszenia, aby wartość była nieujemna. W takim przypadku należy podać wartość -1, zostanie ona przekształcona do pewnej gigantycznej wartości i przekazana jako argument szablonu, kompilator zawiesi się, a użytkownik nie będzie wiedział, co robić. Na szczęście nadchodzące C++ 0x zapewnia statyczny assert z niestandardowym komunikatem do zgłaszania w przypadku niepowodzenia asercji. –

5

Możesz użyć makro BOOST_STATIC_ASSERT. Lub implementuj własny, najprostszym sposobem wymuszenia niepowodzenia jest wykonanie typedef tablicy N elementów, gdzie N jest dodatnie/ujemne w zależności od argumentu.

Problem z tym podejściem polega na tym, że spowoduje niepowodzenie, ale mimo to spróbuje wykonać rekursję. Spójrz na boost::enable_if_c, aby użyć SFINAE do niepowodzenia tworzenia szablonu, jeśli argument jest ujemny.

1

Co powiesz na implementację makra STATIC_CHECK?

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

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

Wewnątrz 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; 
+0

Wadą tego rozwiązania jest to, że sprawdzanie jest niezależne od 'struct'. – sharptooth

2

można przekazać realizację do klasy także przyjmowanie parametru bool wskazującą, czy wynik może być obliczona.

#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; 
} 
Powiązane problemy