2.1.1
除去语法规范等,包括标准的库的接口函数定义、相关的类型、宏、常量等也都会被发布在语言标准中
#include<iostream>
using namespace std;
int main()
{
cout << "Standard Clib:" << __STDC_HOSTED__ << endl;
cout << "Standard C:" << __STDC__ << endl;
cout << "Standard Clib:" << __STDC_HOSTED__ << endl;
cout << "ISO/IEC:" << __STDC_ISO_10646__ << endl;
return 0;
}
输出结果:
2.4 宏 __cplusplus
在c与c++混合编写的代码中,我们常常会在头文件中看到如下声明
#ifdef __cplusplus
extern "C"{
#endif
//一些代码
#ifdef __cplusplus
}
#endif
这种类型的头文件可以被#include到C文件中进行编译,也可以被#include到C++ 文件中进行编译。由于extern"C"可以抑制C++对函数名、变量名等符号(symbol)进行名称重整(namemangling) ,因此编译出的C目标文件和C++目标文件中的变量、函数名称等符号都是相同的(否则不相同),链接器可以可靠地对两种类型的目标文件进行链接。这样该做法成为了C与C++混用头文件的典型做法。
鉴于以上的做法,程序员可能认为__cplusplus这个宏只有“被定义了”和“未定义”两种状态。事实上却并非如此,_cplusplus这个宏通常被定义为一个整型值。而且随着标准变化,_cplusplus宏一般会是一个比以往标准中更大的值。比如在C++03标准中,_cplusplus的值被预定为199711L,而在C++11标准中,宏_cplusplus被预定义为201103L,这点变化可以为代码所用。比如程序员在想确定代码是使用支持C++11编译器进行编译时那么可以按下面的方法进行检测:
#if __cplusplus < 201103L
#error "should use C++11 implementationt"
这里,使用了预处理指令#error,这使得不支持C++11的代码编译立即报错并终止编译。读者可以使用C++98编译器和C++11的编译器分别实验一下其效果。
2.5 静态断言
断言(assertion) 是一种编程中常用的手段。在通常情况下,断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况。比如一个函数总需要输人在一定的范围内的参数,那么程序员就可以对该参数使用断言,以迫使在该参数发生异常的时候程序退出,从而避免程序陷入逻辑的混乱。
但是普通的断言assert宏只有在程序运行时才能起作用,而#error
只在编译器预处理时才起作用。有的时候,我们希望在编译时能做一些断言。比如编写一个函数并使用了断言,但是没有调用,我们无法触发改断言。实际上,++正确产生断言的时机应该在模板实例化时,既编译时期++。而解决方案是进行编译时期的断言,既所谓的“静态断言”。比较典型的的静态断言实现是开源库Boost内置的BOOST_STATIC_ASSERT
断言机制(利用sizeof操作符),我们可以利用“除0”会导致编译器报错的这个特性来实现静态断言。
#include<cstring>
#include<boost/static_assert.hpp>
using namespace std;
#define assert_static(e) \
do { \
enum {assert_static__ = 1/(e) }; \
}while(0)
template <typename T ,typename U>
int bit_copy(T &a, U &b)
{
//assert_static(sizeof(a) == sizeof(b));
BOOST_STATIC_ASSERT(sizeof(a),sizeof(b));
memcpy(&a,&b,sizeof(b));
}
int main()
{
int a = 0x2468;
double b;
bit_copy(a,b);
return 0;
}
结果如我们预期的,在模板实例化时我们会得到编译器的错误报告,在我们的实验机上会输出比较长的错误信息,主要信息是除零错误。当然,读者也可以尝试一下Boost库内置的BOOST_STATIC_ASSERT,输出的主要信息是sizeof错误。但无论是哪种方式的静态断言,其缺陷都是很明显的:诊断信息不够充分,不熟悉该静态断言实现的程序员可能一时无法将错误对应到断言错误上,从而难以准确定位错误的根源。
在C++11标准中,引人了static_assert
断言来解决这个问题。++static_assert使用起来非常简单,它接收两个参数,一个是断言表达式,这个表达式通常需要返回一个bool值;++ 一个则是警告信息,它通常也就是一段字符串。我们可以用static_assert
替换一下代码中bit_copy
的声明。
#include<cstring>
#include<boost/static_assert.hpp>
using namespace std;
#define assert_static(e) \
do { \
enum {assert_static__ = 1/(e) }; \
}while(0)
template <typename T ,typename U>
int bit_copy(T &a, U &b)
{
//assert_static(sizeof(a) == sizeof(b));
//BOOST_STATIC_ASSERT(sizeof(a),sizeof(b));
static_assert(sizeof(a) == sizeof(b), "the parameters of bit_copy must have same width");
memcpy(&a,&b,sizeof(b));
}
int main()
{
int a = 0x2468;
double b;
bit_copy(a,b);
return 0;
}
结果下图所示,无法编译成功,并且有很明显的错误提示。
++但是需要注意的是,static_assert
表达式的结果必须是在编译期间可以计算的表达式,也就是说是常量表达式,如果使用了变量,则会导致错误。++
int positive(const int n )
{
static_assert(n > 0,"value must > 0");
}