C++程序生命周期里存在"编码阶段"和"预处理阶段",它的主要工作是生成人类可识别的源码。而"编译阶段"的目标是生成计算机可识别的机器码。
"预处理编程"是指通过预处理指令如#define
,#include
来控制预处理器的。
与此同时存在控制编译器的"编译指令"。
gcc中的__attribute__
就是编译指令,告知编译器要对齐其修饰的结构体内存。
C++ 11中的属性即是和__attribute__
一样的编译指令,命名为属性。
给变量,函数,类等"贴"上一个编译阶段的标签方便编译器识别处理。
属性的语法是[[...]]
,中间的即是属性标签。
[[noreturn]] // 属性标签
int func(bool flag) // 函数绝不会返回值
{
throw std::runtime_error("xx");
}
C++ 11里仅有两个属性noreturn
和carries_dependency
C++ 14里增加了deprecated
用来标记不推荐使用的变量,函数和类(废弃的)
比如你原来写了一个func1
后来觉得不够好重写了完全不同的新函数func2
,老函数已经被不少人使用了,立即删除不太可能,此时就加个一deprecated
标签,这样在编译时如果用到了这个函数就会输出告警。
deprecated
:与 C++14 相同,但可以用在 C++11 里。
unused
:用于变量、类型、函数等,表示虽然暂时不用,但最好保留着,因为将来可能
会用。
constructor
:函数会在 main() 函数之前执行,效果有点像是全局对象的构造函数。
destructor
:函数会在 main() 函数结束之后执行,有点像是全局对象的析构函数。
always_inline
:要求编译器强制内联函数,作用比 inline 关键字更强。
hot
:标记“热点”函数,要求编译器更积极地优化
nodiscard
: c++17里面个人认为很重要的一个属性,一般用于修饰函数,告诉函数调用者必须关注该函数的返回值(即不能丢弃该函数的返回值)。如果函数调用者未将该函数的返回值赋值给一个变量,则编译器会给出一个警告。
先看assert(动态断言)用来断言一个表达式必定为真。
assert(i > 0 && "i must be greater than zero");
程序运行到assert语句时会计算表达式的值,为false则会输出错误消息,然后调用abort终止程序的执行。
静态断言则相反,不会在运行时生效只会在编译时生效。
它是在编译阶段里检测各种条件的“断言”,编译器看到static_assert
也会计算表达式的值,如果值是false就会报错导致编译失败。
比如计算斐波那契数列的模板元编程风格代码如下:
#include <cstdio>
template <int n>
struct factorial {
static_assert(
n >= 0,
"Arg must be non-negative");
static const int value =
n * factorial<n - 1>::value;
};
template <>
struct factorial<0> {
static const int value = 1;
};
int main()
{
printf("%d\n", factorial<10>::value);
}
注意普通的factorial模板函数,可以通过静态断言保证模板参数必须大于等于零。
同时要注意静态断言运行在编译阶段,只能看到编译时的常数和类型。看不到运行时的变量、指针、内存数据等。