C++11 静态断言(static_assert)
简介
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>>;
相关比较
我们知道,C++现有的标准中,就有assert、#error两个设施,也是用来检查错误的,还有一些第三方的静态断言实现。
assert是运行期断言,它用来发现运行期间的错误,不能提前到编译期发现错误,也不具有强制性,也谈不上改善编译信息的可读性,既然是运行期检查,对性能当然是有影响的,所以经常在发行版本中,assert都会被关掉;
#error可看做预编译期断言,甚至都算不上断言,仅仅能在预编译时显示一个错误信息,它能做的不多,可以参与预编译的条件检查,由于它无法获得编译信息,当然就做不了更进一步分析了。
那么,在stastic_assert提交到C++0x标准之前,为了弥补assert和#error的不足,出现了一些第三方解决方案,可以作编译期的静态检查,例如BOOST_STATIC_ASSERT和LOKI_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++11 静态断言(static_assert)的更多相关文章
- c++11 静态断言
c++11 静态断言 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #includ ...
- 转:探索C++0x: 1. 静态断言(static_assert)
转自:http://www.cppblog.com/thesys/articles/116985.html 简介 C++0x中引入了static_assert这个关键字,用来做编译期间的断言,因此叫做 ...
- (五)静态断言(下),static_assert
二.静态断言与static_assert 通过上一篇,我们可以看到,断言assert宏只有在程序运行的时候才能起作用.而#error值在编译器预处理时才能起作用. 有时候,我们希望在编译时候能做一些断 ...
- C++断言与静态断言
断言是很早之前就有的东西了,只需要引入cassert头文件即可使用.往往assert被用于检查不可能发生的行为,来确保开发者在调试阶段尽早发现“不可能”事件真的发生了,如果真的发生了,那么就表示代码的 ...
- C++11静态assert
[C++11静态assert] C++11新的关键字static_assert可以解决模板中的状态检察. 声明采取以下的形式: 这里有一些如何使用static_assert的例子: 当常数表达式值为f ...
- C++除法运算 // 静态断言
1.C++中"/"运算:对两个整数做除法,结果仍为整数,如果它的商包含小数部分,则小树部分会被截除. C++ Primer 第五章 P130 2.静态断言(static_asser ...
- c语言静态断言-定义自己的静态断言
c语言里面可以自己定义静态断言,更加方便的调试代码. 使用静态断言 #include<stdio.h> #include<stdlib.h> #include<asser ...
- c语言静态断言
在php中可以通过xdebug来显示详细的错误信息,可以细化到哪个文件哪行代码引起的报错.在C语言里面也可以通过静态断言(assert)来使得调试代码更加方便.关于断言,可以作为一种很强大的调试方式或 ...
- C语言中静态断言的使用
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作异常处理的高级形式,用于代码调试. #define _CRT_SECURE_NO_WARNINGS //关闭安全监察 ...
随机推荐
- 函数返回char* 的解决方案
在C语言中,自动变量在堆栈中分配内存.当包含自动变量的函数或代码块退出时,它们所占用的内存便被回收,它们的内容肯定会被下一个所调用的函数覆盖.这一切取决于堆栈中先前的自动变量位于何处,活动函数声明了什 ...
- Android Studio使用教程(一)
今年的Google全球开发者大会虽然没有新的Android系统和设备,但是还是推出了一些不错的产品,Android Studio就是其中之一.这个基于Intellij IDEA开发的Android I ...
- (四)《Java编程思想》——可变参数列表
以object数组为参数的方法实现可变参数列表 package chapter5; /** * 以object数组为参数的方法实现可变参数列表 */ class A { } public class ...
- Visual Studio 2015 RC Downloads
https://www.visualstudio.com/en-us/downloads/visual-studio-2015-downloads-vs
- ListView自定义滑动条
/** * 修改默认滑动条 */ private void SetSliderIcon() { try { Field f = AbsListView.class.getDeclaredField(& ...
- Centos6.5使用yum安装Mysql5.7
想要玩新的东东就要付出代价,我的时间悄悄的都溜走了,说多了都是泪! 实践才是真理! 系统版本:Linux localhost.localdomain 2.6.32-431.el6.x86_64 #1 ...
- 一种用css实现图片在父框中等比缩放并垂直居中的办法
一个网页中往往会有很多图片,而网站的编辑上传图片时可能并不一定按照为父框设定的那个宽高来传,这样图片往往会将父框撑开或者被父框截断.一种比较好的解决的办法是这样的: HTML代码结构: <div ...
- 在Yii2中使用Pjax导致Yii2内联脚本载入失败的问题
当我用defunkt/jquery-pjax载入Yii2的ActiveForm时发生一个错误,正常情况下是 ActiveForm的两个js应该先载入,而实际情况是 typeError:JQuery(. ...
- git基础使用小记
一.安装步骤省略二.运行“Git Bash“在打开的窗口中输入:ssh-keygen -t rsa -C "my@gmail.com" 会提示SSH Public Keys存放的位 ...
- win7安装memcached
根据公司业务需求,需要用memcache缓存,正好接触一下,在win7下配置安装: 1. 下载memcache的windows稳定版,解压放某个盘下面,比如在c:\memcached 2. 在终端(也 ...