如果需要保證類型T包含一個名爲value的常量或名爲type的類型,可以這樣寫:
template <typename T>
void myfunc()
{
typedef typename T::type ERROR_T_DOES_NOT_CONTAIN_type;
const int ASSERT_T_MUST_HAVE_STATIC_CONSTANT_value(T::value);
};
那麼如果傳進來的T沒有value或沒有type,就會報錯。
複雜的斷言通過不完整類型不能構造,或者如果T是不完整類型,sizeof(T)導致編譯錯誤來實現。
布爾斷言
template <bool STATEMENT>
struct static_assertion
{};
template <>
struct static_assertion<false>;
對false的特化沒有實現,如果傳入一個false值,將會報一個編譯期錯誤。
用宏封裝一下:
#define MXT_ASSERT(statement) sizeof(static_assertion<(statement)>)
宏的問題是逗號:
MXT_ASSERT(is_well_defined< std::map<int, double> >::value);
int的逗號前面的部分會識別爲宏的第一個參數,後面的部分會識別爲宏的第二個參數。
解決方法1:用typedef
typedef std::map<int, double> map_type;
MXT_ASSERT( is_well_defined<map_type>::value );
解決方法2:加括號
MXT_ASSERT(( is_well_defined< std::map<int, double> >::value ));
static_assertion可以繼承:
template <typename T>
class small_object_allocator : static_assertion<(sizeof(T)<64)>
{};
因爲static_assertion是一個類模板嘛。
斷言合法性
如果要檢查一些表達式的有效性,並且返回非void:
#define MXT_ASSERT_LEGAL(statement) sizeof(statement)
如果允許返回void:
#define MXT_ASSERT_LEGAL(statement) sizeof((statement), 0)//因爲void是不完整類型
如果要檢查T是否有一個函數empty:
template <typename T>
void do_something(T& x)
{
MXT_ASSERT_LEGAL(static_cast<bool>(x.empty()));
if (x.empty())
{
// ...
}
}
其他的應用包括:
#define MXT_CONST_REF_TO(T) (*static_cast<const T*>(0))/**驗證解const引用合法,
static_cast<const T*>(0)得到一個const T*,
*static_cast<const T*>(0)是解引用,下面同理*/
#define MXT_REF_TO(T) (*static_cast<T*>(0))//驗證解引用合法
template <typename obj_t, typename iter_t>
class assert_iterator
{
enum
{
verify_construction =
MXT_ASSERT_LEGAL(obj_t(*MXT_CONST_REF_TO(iter_t))), /**驗證拷貝構造函數合法
MXT_CONST_REF_TO(iter_t)得到一個iter_t對象,
*MXT_CONST_REF_TO(iter_t)得到一個obj_t對象,
obj_t(*MXT_CONST_REF_TO(iter_t))就是拷貝構造,下面同理*/
verify_assignment =
MXT_ASSERT_LEGAL(MXT_REF_TO(obj_t) = *MXT_CONST_REF_TO(iter_t)),//驗證賦值操作符合法
verify_preincr =
MXT_ASSERT_LEGAL(++MXT_REF_TO(iter_t)),//驗證前置自增合法
verify_postincr =
MXT_ASSERT_LEGAL(MXT_REF_TO(iter_t)++)//驗證後置自增合法
};
};
用函數指針建模concept
template <typename T1, typename T2>
struct static_assert_can_copy_T1_to_T2
{
static void concept_check(T1 x, T2 y)
{
T2 z(x); // T2 must be constructable from T1
y = x; // T2 must be assignable from T1
}
static_assert_can_copy_T1_to_T2()
{
void (*f)(T1, T2) = concept_check;
}
};
通過派生或生成實例來使用
template <typename T>
T sqrt(T x)
{
static_assert_can_copy_T1_to_T2<T, double> CHECK1;
}
template <typename T>
class math_operations : static_assert_can_copy_T1_to_T2<T, double>
{};
這裏解釋一下。當生成CHECK1對象的時候,就會調用static_assert_can_copy_T1_to_T2的構造函數。類模板的成員函數在用到的時候纔會生成對應的代碼,構造函數裏用到concept_check,所以生成concept_check的代碼,並且檢查T2 z(x);y=x;的合法性。
標籤技術
在標準庫的例子如iterator。標籤的好處是構建臨時標籤對象代價很小,並且只編譯需要的功能。
類型標籤
struct naive_algorithm_tag {};
struct precise_algorithm_tag {};
template <typename T>
inline T log1p(T x, naive_algorithm_tag);
template <typename T>
inline T log1p(T x, precise_algorithm_tag);
也可以是模板標籤:
template <int N>
struct algorithm_precision_level {};
template <typename T, int N>
inline T log1p(T x, algorithm_precision_level<N>);
// ...
double x = log1p(3.14, algorithm_precision_level<4>());
函數標籤
enum algorithm_tag_t
{
NAIVE,
PRECISE
};
inline static_value<algorithm_tag_t, NAIVE> naive_algorithm_tag()
{
return 0;
}
inline static_value<algorithm_tag_t, PRECISE> precise_algorithm_tag()
{
return 0;
}
標籤是函數指針,好處是可以爲相同的運行時和編譯時算法提供統一的語法。但感覺很少寫運行時+編譯時兩套實現,暫時跳過。
標籤迭代
用0填充數組:
template <typename T, int N>
void zeroize_helper(T* const data, static_value<int, N>)
{
zeroize_helper(data, static_value<int, N-1>());
data[N-1] = T();
}
template <typename T>
void zeroize_helper(T* const data, static_value<int, 1>)
{
data[0] = T();
}
template <typename T, int N>
void zeroize(T (&data)[N])
{
zeroize_helper(data, static_value<int, N>());
}
或者可以調換一下語句的順序:
template <typename T, int N>
void zeroize_helper(T* const data, static_value<int, N>)
{
data[N-1] = T();
zeroize_helper(data, static_value<int, N-1>());
}
這是線性的,也可以以指數的形式,假設N是2的冪:
template <int N, int M>
struct index
{
};
template <typename T, int N, int M>
void zeroize_helper(T* const data, index<N, M>)
{
zeroize_helper(data, index<N/2, M>());
zeroize_helper(data, index<N/2, M+N/2>());
}
template <typename T, int M>
void zeroize_helper(T* const data, index<1, M>)
{
data[M] = T();
}
template <typename T, int N>
void zeroize(T (&data)[N])
{
zeroize_helper(data, index<N, 0>());
}
double test[8];
zeroize(test);
展開如下: