C++中#error/assert/static_assert的区别及使用

时间:2021-08-12 00:40:56

C++ 语言支持可帮助您调试应用程序的三个错误处理机制:#error 指令、static_assert 关键字和 assert (CRT) 宏。所有的三种机制都会发出错误消息。

#error可看做预编译期断言,甚至都算不上断言,仅仅能在预编译时显示一个错误信息,它能做的不多,可以参与预编译的条件检查,由于它无法获得编译信息,当然就做不了更进一步分析了。使用#error方法是非常烦琐的,并且不能够对模板参数进行检查,因为模板实例化是在编译时进行,而#error方法是在预处理阶段进行的。

#error 指令在预处理时有效。它将无条件地发出用户指定的消息并导致编译因错误而失败。该消息可包含由预处理器指令操作的文本,但不会计算任何生成的表达式。

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

assert (CRT) 宏在运行时有效。它会计算用户指定的表达式,如果结果为零,系统将发出诊断消息并关闭应用程序。

static_assert语法:static_assert(constant-expression,string-literal);

static assert is used to make assertions at compile time. When the static assertion fails, the program simply doesn't compile.

static_assert这个宏用于检测和诊断编译时错误。编译期,这是一个与 CRT-assert(运行时宏)相反的宏。这个宏用于检测编译时程序的不变量。static_asset 是在编译时执行的,不能用于检测运行时的值。而断言assert宏只有在程序运行时才能起作用。

static_assert宏,在编译时测试软件断言。如果指定的常量表达式为false,则编译器显示指定的消息,并且编译失败,错误为 C2338;否则,声明不起作用。

static_assert 声明的constant-expression 参数表示软件断言。软件断言指定在程序的某个特定点应满足的条件。如果满足该条件,则 static_assert 声明无效。如果未满足该条件,则断言失败,编译器在 string-literal 参数中显示消息,并且编译因出错而失败。

static_assert 声明在编译时测试软件断言。相反,assert (CRT) 宏在运行时测试软件断言,并会导致增大运行时花费的空间和时间。由于模板参数包含在constant-expression 参数中,因此 static_assert 声明对于调试模板很有用。

当遇到声明时,编译器将检查static_assert 声明是否存在语法错误。如果编译器不依赖于模板参数,则编译器会立即计算 constant-expression 参数。否则,在对模板进行实例化时,编译器将计算 constant-expression 参数。因此,当遇到声明时,编译器可能一次发布一个诊断消息,而在对模板进行实例化时也是如此。

可以在命名空间、类或块范围中使用static_assert 关键字。(由于 static_assert 关键字可以在命名空间范围内使用,因此,即使它不将新名称引入程序中,但从技术上讲,它也是一个声明。)

使用static_assert,我们可以在编译期间发现更多的错误,用编译器来强制保证一些契约,并帮助我们改善编译信息的可读性,尤其是用于模板的时候。static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算,但如果该常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能。

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

static_assert声明在编译时有效。它将测试由用户指定且可以转换为布尔值的整数表达式表示的软件断言。如果表达式的计算结果为零 (false),编译器将发出用户指定的消息,并且编译因错误而失败。static_assert 声明对调试模板尤其有用,因为模板参数可包含在用户指定的表达式中。

以下是测试代码:

static_assert.hpp:

#ifndef FBC_MESSY_TEST_STATIC_ASSERT_HPP
#define FBC_MESSY_TEST_STATIC_ASSERT_HPP

#ifndef __cplusplus
#error static_assert.hpp header must be compiled as C++
#endif

#include <type_traits>
#include <iosfwd>
#include <cassert>

// reference: https://msdn.microsoft.com/zh-cn/library/dd293588.aspx

// static_assert 声明具有类范围。 static_assert 验证模板参数是否为纯旧数据 (POD) 类型。
// 编译器将在声明 static_assert 声明时检查该声明,但不计算 constant-expression 参数,直到在 main() 中实例化 basic_string 类模板
template <class CharT, class Traits = std::char_traits<CharT> >
class basic_string {
static_assert(std::tr1::is_pod<CharT>::value, "Template argument CharT must be a POD type in class template basic_string");
// ...
};

struct NonPOD {
NonPOD(const NonPOD &) {}
virtual ~NonPOD() {}
};

// reference: http://*.com/questions/1647895/what-does-static-assert-do-and-what-would-you-use-it-for
class Foo
{
public:
static const int bar = 5; // 3
};

static_assert(Foo::bar > 4, "Foo::bar is too small :(");


void test_static_assert1();
void test_static_assert2();
void test_static_assert3();
void test_static_assert4(int a);

#endif // FBC_MESSY_TEST_STATIC_ASSERT_HPP
static_assert.cpp:

#include "static_assert.hpp"void test_static_assert1(){// static_assert 声明具有命名空间范围。由于编译器知道类型 void * 的大小,因此可以立即计算表达式//该static_assert用来确保编译仅在32位的平台上进行,不支持64位的平台//static_assert(sizeof(void *) == 4, "64-bit code generation is not supported."); // sizeof(void *) = 8 ?}void test_static_assert2(){basic_string<char> bs;}void test_static_assert3(){Foo::bar;}void test_static_assert4(int a){assert(a / 2 == 0);}

主要参考文献:

1. https://msdn.microsoft.com/zh-cn/library/dd293588.aspx


GitHubhttps://github.com/fengbingchun/Messy_Test