编译阶段能做什么:属性和静态断言

2021-06-20  本文已影响0人  Drew_MyINTYRE

“编译阶段”的目标是生成计算机可识别的机器码。

编译是预处理之后的阶段,它的输入是(经过预处理的)C++ 源码,输出是二进制可执行文件(也可能是汇编文件、动态库或者静态库)。这个处理动作就是由编译器来执行的。

有没有用来控制编译器的“编译指令”呢?如果有这么一个东西,让程序员来手动指示编译器这里该如何做、那里该如何做,就有可能会生成更高效的代码。

到了 C++11,标准委员会终于认识到了“编译指令”的好处,于是就把“民间”用法升级为“官方版本”,起了个正式的名字叫“属性”。你可以把它理解为给变量、函数、类等“贴”上一个编译阶段的“标签”,方便编译器识别处理。
“属性”没有新增关键字,而是用两对方括号的形式“[[…]]”,方括号的中间就是属性标签,注意属性的编写规范,作用范围。

[[noreturn]]              // 属性标签
int func(bool flag)       // 函数绝不会返回任何值
{
    throw std::runtime_error("XXX");
}

 //  声明变量暂不使用,不是错误
[[gnu::unused]]     
int nouse;  

 // C++14 or later
// 当然,程序还是能够正常编译的,但这种强制的警告形式会“提醒”用户旧接口已经被废弃了,应该尽快迁移到新接口。
[[deprecated("deadline:2020-12-31")]]     
int old_func();

下面我就列出几个比较有用的(全部属性可参考GCC 文档),GCC 的属性都在 “gnu::” 里。

静态断言(static_assert)

static_assert 可以在编译阶段定义各种前置条件,充分利用 C++ 静态类型语言的优势,让编译器执行各种检查,避免把隐患带到运行阶段。

“静态断言”,static_assert (它是一个关键字),而不是宏。因为它只在编译时生效,运行阶段看不见,所以是“静态”的。它是编译阶段里检测各种条件的“断言”,编译器看到 static_assert 也会计算表达式的值,如果值是 false,就会报错,导致编译失败。

static_assert(
  sizeof(long) >= 8, "must run on x64");
  
static_assert(
  sizeof(int)  == 4, "int must be 32bit");

//下面的代码想检查空指针,由于变量只能在运行阶段出现,而在编译阶段不存在,所以静态断言无法处理。
char* p = nullptr;
static_assert(p == nullptr, "some error.");  // 错误用法

在泛型编程的时候,怎么检查模板类型呢?比如说,断言是整数而不是浮点数、断言是指针而不是引用、断言类型可拷贝可移动等等?

想要更好地发挥静态断言的威力,还要配合标准库里的“type_traits”,它提供了对应这些概念的各种编译期“函数”。

// 假设 T 是一个模板参数,即 template<typename T>
static_assert(
  is_integral<T>::value, "int");

static_assert(
  is_pointer<T>::value, "ptr");

static_assert(
  is_default_constructible<T>::value, "constructible");

上一篇 下一篇

猜你喜欢

热点阅读