转载请注明来源:http://blog.csdn.net/thesys/archive/2010/06/02/5641350.aspx

简介

C++0x中引入了static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言。

其语法很简单:static_assert(常量表达式,提示字符串)。

如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串。

说明

使用static_assert,我们可以在编译期间发现更多的错误,用编译器来强制保证一些契约,并帮助我们改善编译信息的可读性,尤其是用于模板的时候。

static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。

编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算,但如果该常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能。

由于之前有望加入C++0x标准的concepts提案最终被否决了,因此对于检查模板参数是否符合期望的重任,就要靠static_assert来完成了,所以如何构造适当的常量表达式,将是一个值得探讨的话题。

性能方面,由于是static_assert编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失。

范例

下面是一个来自MSDN的简单范例:

static_assert(sizeof(void *) == 4, "64-bit code generation is not supported.");

static_assert用来确保编译仅在32位的平台上进行,不支持64位的平台,该语句可以放在文件的开头处,这样可以尽早检查,以节省失败情况下的编译时间。

再看另一个范例:

   1: struct MyClass
   2: {
   3:     char m_value;
   4: };
   5:  
   6: struct MyEmptyClass
   7: {
   8:     void func();
   9: };
  10:  
  11: // 确保MyEmptyClass是一个空类(没有任何非静态成员变量,也没有虚函数)
  12: static_assert(std::is_empty<MyEmptyClass>::value, "empty class needed");
  13:  
  14: //确保MyClass是一个非空类
  15: static_assert(!std::is_empty<MyClass>::value, "non-empty class needed");
  16:  
  17: template <typename T, typename U, typename V>
  18: class MyTemplate
  19: {
  20:     // 确保模板参数T是一个非空类
  21:     static_assert(
  22:         !std::is_empty<T>::value,
  23:         "T should be n non-empty class"
  24:         );
  25:  
  26:     // 确保模板参数U是一个空类
  27:     static_assert(
  28:         std::is_empty<U>::value,
  29:         "U should be an empty class"
  30:         );
  31:  
  32:     // 确保模板参数V是从std::allocator<T>直接或间接派生而来,
  33:     // 或者V就是std::allocator<T>
  34:     static_assert(
  35:         std::is_base_of<std::allocator<T>, V>::value,
  36:         "V should inherit from std::allocator<T>"
  37:         );
  38:  
  39: };
  40:  
  41: // 仅当模板实例化时,MyTemplate里面的那三个static_assert才会真正被演算,
  42: // 藉此检查模板参数是否符合期望
  43: template class MyTemplate<MyClass, MyEmptyClass, std::allocator<MyClass>>;
通过这个例子我们可以看出来,static_assert可以很灵活的使用,通过构造适当的常量表达式,我们可以检查很多东西。比如范例中std::is_emptystd::is_base_of都是C++新的标准库提供的type traits模板,我们使用这些模板可以检查很多类型信息。

 

相关比较

我们知道,C++现有的标准中,就有assert、#error两个设施,也是用来检查错误的,还有一些第三方的静态断言实现。

assert是运行期断言,它用来发现运行期间的错误,不能提前到编译期发现错误,也不具有强制性,也谈不上改善编译信息的可读性,既然是运行期检查,对性能当然是有影响的,所以经常在发行版本中,assert都会被关掉;

#error可看做预编译期断言,甚至都算不上断言,仅仅能在预编译时显示一个错误信息,它能做的不多,可以参与预编译的条件检查,由于它无法获得编译信息,当然就做不了更进一步分析了。

那么,在stastic_assert提交到C++0x标准之前,为了弥补assert#error的不足,出现了一些第三方解决方案,可以作编译期的静态检查,例如BOOST_STATIC_ASSERTLOKI_STATIC_CHECK,但由于它们都是利用了一些编译器的隐晦特性实现的trick,可移植性、简便性都不是太好,还会降低编译速度,而且功能也不够完善,例如BOOST_STATIC_ASSERT就不能定义错误提示文字,而LOKI_STATIC_CHECK则要求提示文字满足C++类型定义的语法。

译器实现

gcc和vc的实现没有太大的差别,均不支持中文提示,非常遗憾,而且VC仅支持ASCII编码,L前缀就会编译出错。GCC好像可以支持其他编码,例如L前缀和U前缀,但我试过发现结果和ASCII编码一样。

static_assert的错误提示,VC比GCC稍微友好一些,VC对上下文和调用堆栈都有较清晰描述,相比之下,GCC的提示简陋一些,但也算比较明确吧,本来么,static_assert很大程度上就是为了编译器的提示信息更加友好而存在的。

应用研究

最后再举个例子,用来判断某个类是否有某个指定名字的成员,供参考和体验。其实static_assert的应该很大程度上就是看如何构造常量表达式了,这个例子中使用了decltype关键字(也是C++0x新特性)和SFINAE技巧,以及一些编译器相关的技巧(主要是解决VC编译器的bug),具体的技术细节和原理在今后的文章中还会仔细探讨。

   1: // 判断类是否含有foo这个成员变量和成员函数
   2: // 针对GCC的实现,基本上就是针对标准C++的实现
   3: #ifdef __GNUC__
   4:  
   5: // check_property_foo函数的两个重载版本,结合decltype,
   6: // 通过SFINAE在编译期推导指定类型是否含有foo这个成员变量
   7: char check_property_foo(...);
   8:  
   9: template <typename T>
  10: void* check_property_foo(T const& t, decltype(&(t.foo)) p = 0);
  11:  
  12: // 这个类模板通过check_property_foo得出T是否含有foo这个成员变量
  13: template <typename T>
  14: struct has_property_foo : public std::integral_constant<
  15:     bool, sizeof(check_property_foo(*static_cast(0))) == sizeof(void*)>
  16: {
  17: };
  18:  
  19: // check_method_foo函数的两个重载版本,结合decltype,
  20: // 通过SFINAE在编译期推导指定类型是否含有foo这个成员函数
  21: char check_method_foo(...);
  22:  
  23: template <typename T>
  24: void* check_method_foo(T const& t, decltype(&(T::foo)) p = 0);
  25:  
  26: // 这个类模板通过check_method_foo得出T是否含有foo这个成员函数
  27: template <typename T>
  28: struct has_method_foo  : public std::integral_constant<
  29:     bool, !has_property_foo::value &&
  30:     sizeof(check_method_foo(*static_cast(0))) == sizeof(void*)>
  31: {
  32: };
  33: #endif
  34:  
  35: // 针对VC的实现,由于VC编译器在处理decltype和SFINAE情况下存在bug,
  36: // 我们只能采用一些花招来绕过这个bug
  37: #ifdef _MSC_VER
  38:  
  39: // check_member_foo函数的两个重载版本,结合decltype,
  40: // 通过SFINAE在编译期推导指定类型是否含有foo这个成员变量或函数
  41: char check_member_foo(...);
  42: 
  43: template <typename T>
  44: auto check_member_foo(T const& t, decltype(&(t.foo)) p = 0)->decltype(p);
  45:  
  46: // 这个类模板通过check_member_foo得出T是否含有foo这个成员变量
  47: template <typename T>
  48: struct has_property_foo
  49: {
  50:     static const bool value =
  51:         sizeof(check_member_foo(*static_cast(0))) != sizeof(char) &&
  52:         std::is_pointerstatic_cast(0)))>::value;
  53: };
  54:  55: // 这个类模板通过check_member_foo得出T是否含有foo这个成员函数
  56: template <typename T>
  57: struct has_method_foo
  58: {
  59:     static const bool value =
  60:         sizeof(check_member_foo(*static_cast(0))) != sizeof(char) &&
  61:         !std::is_pointerstatic_cast(0)))>::value;
  62: };
  63:  
  64: #endif
  65:  
  66: // 先定义几个类供实现检测
  67: struct WithPropertyFoo
  68: {
  69:     int foo;
  70: };
  71:  
  72: struct WithMethodFoo
  73: {
  74:     void foo();
  75: };
  76:  
  77: struct WithRefPorpertyFoo
  78: {
  79:     int& foo;
  80: };
  81:  
  82: struct WithoutFoo
  83: {
  84:     void bar();
  85: };
  86:  
  87: // 用static_assert对这些条件进行检查
  88: static_assert(has_property_foo::value, "property foo needed");
  89: static_assert(has_method_foo::value, "method foo needed");
  90: static_assert(!has_property_foo::value, "no property foo");
  91: static_assert(!has_method_foo::value, "no methoed foo");
  92: static_assert(has_property_foo::value, "property foo needed");
  93: static_assert(!has_method_foo::value, "no method foo");

探索C++0x: 1. 静态断言(static_assert)相关推荐

  1. c++11中静态断言static_assert

    c++11中的静态断言(static_assert) 在c++11中引入了,目的是在编译时就能检查处一些问题.写法如下: static_assert(常量表达式,提示字符串); 如果第一个参数常量表达 ...

  2. C++11的静态断言

    断言就是将一个返回值总是需要为真的判别式放在语句中,来排除在设计的逻辑上不应该出现的情况.C++11标准中引入了静态断言:static_assert 在C++标准中,<cassert>或a ...

  3. C++笔记-断言、静态断言、R转义符

    目录 断言 静态断言 R转义符 断言 在assert.h和cassert中有这3个短语,可以获取当前文件,第几行.哪个函数. 感觉这个在Linux调试上十分有用,在此记录下! 程序运行截图如下: 源码 ...

  4. C++断言与静态断言

    断言是很早之前就有的东西了,只需要引入cassert头文件即可使用.往往assert被用于检查不可能发生的行为,来确保开发者在调试阶段尽早发现"不可能"事件真的发生了,如果真的发生 ...

  5. c语言 静态断言,C断言/静态断言

    关于断言,可以作为一种很强大的调试方式或者程序运行时的错误诊断 但是断言也不是适合于各种地方,服务器软件和嵌入式程序一般不适用,断言会强制中断正在运行的程序,对于服务器等程序来说, 将会是一个灾难.加 ...

  6. C和C++安全编码笔记:整数安全

    5.1 整数安全导论:整数由包括0的自然数(0, 1, 2, 3, -)和非零自然数的负数(-1, -2, -3, -)构成. 5.2 整数数据类型:整数类型提供了整数数学集合的一个有限子集的模型.一 ...

  7. 手把手写C++服务器(0):专栏文章-汇总导航【持续更新】

    手把手写C++服务器(1):网络编程常见误区 手把手写C++服务器(2):C/C++编译链接模型.函数重载隐患.头文件使用规范 手把手写C++服务器(3):C++编译常见问题.编译优化方法.C++库发 ...

  8. C++11新特性精讲(多线程除外)

    文章目录 1.什么是 C+11 2.类型推导 2.1 auto的使用 2.2 auto的易错点 2.3 decltype 2.4 追踪返回类型 3.易用性的改进 3.1 初始化 3.1.1 类中内成员 ...

  9. C和C++安全编码笔记:总结

    <C和C++安全编码>(原书第2版)这本书是2013年出版的. 这里是基于之前所有笔记的简单总结,笔记列表如下: 字符串:https://blog.csdn.net/fengbingchu ...

最新文章

  1. halcon与QT联合:(5.1)瓶盖检测以及QT界面搭建
  2. 学习OpenCV——Surf(特征点篇)flann快速最近邻搜索算法
  3. ITK:侵蚀灰度图像
  4. A multi-faceted language for the Java platform
  5. python两个数相加时_两数相加 leetcode Python
  6. A Convolutional Neural Network for Modelling Sentences阅读笔记
  7. .NET Core 反射获取所有控制器及方法上特定标签
  8. 手动安装boost库
  9. 矩阵 II : 线性组的线性相关性
  10. java tcp ack_TCP三次握手和四次挥手以及11种状态
  11. ASP.Net缓存总结
  12. gulp教程之gulp-minify-css
  13. 基于STM32构建EtherCAT主站(SOEM方案)2
  14. 拉里 埃里森_拉里·沃尔(Larry Wall)的“程序员的三个美德”是胡说八道
  15. matlab portcons,马科维茨投资组合理论(均方模型)学习笔记——基于Matlab(二)...
  16. 50 条有趣的 Python 一行代码,建议收藏夹吃灰
  17. RQNOJ 341 星门跳跃【解题报告】
  18. 【学习笔记】Stern-Brocot Tree
  19. 34%的人会出轨。。。
  20. java生成 *.crt和*.key文件与*.keystore *.jks文件的转换

热门文章

  1. vb 计算机主板喇叭发声,主板蜂鸣器发声规律总结
  2. 随笔之与潇哥交谈1h12min内容//2021-1-28【最后一个寒假作业】
  3. oracle外部表报错ora29400,报错ORA-29913 ORA-29400
  4. 一些国外的MD5破解网站
  5. 8255A的工作方式
  6. 42表盘直径是从哪测量_表盘直径多大合适,怎样测量手表表盘的直径
  7. ROS 机器人模型节点的运动控制原理
  8. Keil多分支工程管理
  9. linux安装桌面xmanager,Linux安装图形界面和Vnc与Xmanager服务
  10. Android -- 三种动画(帧动画、View动画、属性动画)