Visual C++ 2010新功能之static_assert(静态断言)
写过模板的同学对下面的这样的代码应该会很熟悉:
template < typename T, unsigned MaxLen >
class static_array
{
private:
typedef DBSOFT_TR1::array< T, MaxLen > array_type;
typedef char __TEMPLATE__CHECK__DUMMY__[sizeof(T)]; // 如果sizeof無法計算T的大小會報錯
typedef char __TEMPLATE__CHECK__LEN_DUMMY__[MaxLen-1]; // 不能用負數、0等來具現化
在编译时它会在我们使用void或者长度小于等于0去具现化这个模板类的时候提示编译错误。以上面的代码为例,如果我们这样去使用这个static_array的话:
#include <dbsoft/generic/static_array.hpp>
int main()
{
dbsoft::static_array<void, 10> aryTest;
return 0;
}
会遇到这样的错误:
1>d:\dbsoft\dbsoft\generic\static_array.hpp(34): error C2070: “void”: 非法的 sizeof 操作数
1> e:\documents\visual studio 2010\projects\helloworld\helloworld\main.cpp(9): 参见对正在编译的类 模板 实例化“dbsoft::static_array<T,MaxLen>”的引用
1> with
1> [
1> T=void,
1> MaxLen=10
1> ]
通过这个提示我们就可以知道我们使用错了,将问题提前暴露出来总是好的,省去了大量的运行时Debug的麻烦。虽然这种方法可以帮助我们做编译时鉴定,但是它的信息始终不是友好的,从VC2010开始,一个新的断言可以帮我们发挥出这种鉴定方式的最大威力,它就是static_assert。
它的声明是这样的:
static_assert( 编译时可推断出结果的表达式, 一个简单的多字节的字符串 )
前者必须是一个编译时可知的表达式或者数值,如果你用一个变量去作为它的第一个参数是会遇到编译错误的,要断言变量可以用assert而非这个静态断言。
遗憾的是static_assert只能支持简单的字符串,即使你在这个字符串中嵌入中文,它也显示不出来。
熟悉Boost的同学都知道Boost也有一个静态断言的库位于boost/static_assert.hpp,不过可惜的是它确实无法和VC2010这个内置的静态断言相比,因为它确实无法嵌入像静态断言那样嵌入更多有用的信息,因为这毕竟需要编译器的内置支持才可以。
#ifdef BOOST_HAS_STATIC_ASSERT
# define BOOST_STATIC_ASSERT( B ) static_assert(B, #B)
#else
当编译器支持静态断言的时候,Boost的静态断言会被自动替换成编译器的静态断言,但是可惜的是它只是简单的将表达式静态的转换成字符串用于友好信息,这不能不说是一个遗憾。当我们需要在各种编译器下面切换编译的时候就用Boost的静态断言吧,如果我们只打算在VC2010或者其后续版本中使用,那么就用编译器的静态断言吧。
不要认为静态断言是鸡肋,只能用于模板中,实际上它的应用面很广。比如下面这种情况,我们要求用户定义的ID必须大于我们保留的ID,否则就出现静态断言:
static const int USER_ID_MIN = 1000;
static const int MsgID = 100;
int main()
{
static_assert( MsgID > USER_ID_MIN, "msg id must > USER_ID_MIN" );
return 0;
}
只要是可以在编译时通过某种方式转换成Bool值的情况,我们都可以用静态断言!这个大家可以慢慢的去发掘,我自己也在发掘其它可以使用的地方,;-)。
当然我们也可以把它用在模板当中,如上面的static_array,可以这样写:
template < typename T, unsigned MaxLen >
class static_array
{
public:
// 默認為空 但是如果是類型的話那麼構造函數會調用MaxLen次
static_array():m_uLen(0)
{
#if _MSC_VER >= 1600
static_assert( std::is_void<T>::value, "can't use void to realize dbsoft::static_array!!");
static_assert( MaxLen > 0, "dbsoft::static_array's template param MaxLen must > 0");
#else
typedef char __TEMPLATE__CHECK__DUMMY__[sizeof(T)]; // 如果sizeof無法計算T的大小會報錯
typedef char __TEMPLATE__CHECK__LEN_DUMMY__[MaxLen-1]; // 不能用負數、0等來具現化
#endif
}
如果用0去具现化的话会:
error C2338: can't use void to realize dbsoft::static_array!!
注意,static_assert并非只能用于函数中:
static const int USER_ID_MIN = 1000;
static const int MsgID = 100;
static_assert( MsgID > USER_ID_MIN, "error!");
int main()
{
return 0;
}
这也是合法的。另外在模板下面,static_assert和头文件<type_traits>结合起来可以迸发出巨大的威力!type_traits是一群特征类的集合,通过特化等方式便捷的为我们提供编译时特征信息,它们大概具有如下的声明方式:
namespace std {
namespace tr1 {
template < class Ty, Ty val > struct integral_constant;
typedef integral_constant < bool, false > false_type ;
typedef integral_constant < bool, true > true_type;
template < class Ty > struct is_void ;
template < class Ty > struct is_integral ;
template < class Ty > struct is_floating_point ;
template < class Ty > struct is_array ;
template < class Ty > struct is_pointer;
template < class Ty > struct is_reference;
template < class Ty > struct is_member_object_pointer;
template < class Ty > struct is_member_function_pointer;
template < class Ty > struct is_enum;
template < class Ty > struct is_union;
template < class Ty > struct is_class;
template < class Ty > struct is_function;
template < class Ty > struct is_arithmetic;
template < class Ty > struct is_fundamental;
template < class Ty > struct is_object;
template < class Ty > struct is_scalar;
template < class Ty > struct is_compound;
template < class Ty > struct is_member_pointer;
template < class Ty > struct is_const;
template < class Ty > struct is_volatile;
template < class Ty > struct is_pod;
template < class Ty > struct is_empty;
template < class Ty > struct is_polymorphic;
template < class Ty > struct is_abstract;
template < class Ty > struct has_trivial_constructor;
template < class Ty > struct has_trivial_copy;
template < class Ty > struct has_trivial_assign;
template < class Ty > struct has_trivial_destructor;
template < class Ty > struct has_nothrow_constructor;
template < class Ty > struct has_nothrow_copy;
template < class Ty > struct has_nothrow_assign;
template < class Ty > struct has_virtual_destructor;
template < class Ty > struct is_signed;
template < class Ty > struct is_unsigned;
template < class Ty > struct rank;
template < class Ty, unsigned I = 0 > struct extent;
template < class Ty1, class Ty2 > struct is_same;
template < class From, class To > struct is_convertible;
template < class Base, class Derived > struct is_base_of;
template < class Ty > struct remove_const;
template < class Ty > struct remove_volatile;
template < class Ty > struct remove_cv;
template < class Ty > struct add_const;
template < class Ty > struct add_volatile;
template < class Ty > struct add_cv;
template < class Ty > struct remove_reference;
template < class Ty > struct add_reference;
template < class Ty > struct remove_pointer;
template < class Ty > struct add_pointer;
template < class Ty > struct remove_extent;
template < class Ty > struct remove_all_extents;
template < class Ty > struct alignment_of;
template < size_t Len, size_t Align > struct aligned_storage;
} };
用法很简单,在模板编程的时候拥有无比巨大的威力。
PS:一个早期版本的static_array.