如果需要保证类型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);
展开如下: