#include <iostream>
#include <random>
template <int min, int max>
int get_number() {
if constexpr (min >= max) {
throw std::invalid_argument("Min. val. must be less than max. val.\n");
}
srand(time(nullptr));
static std::uniform_int_distribution<int> dist{min, max};
std::mt19937 mt{(unsigned int) rand()};
return dist(mt);
}
#include <iostream>
#include <random>
template <int min, int max>
int get_number() {
static_assert(min < max, "Min. value must be less than max. value.\n");
srand(time(nullptr));
static std::uniform_int_distribution<int> dist{min, max};
std::mt19937 mt{(unsigned int) rand()};
return dist(mt);
}
9条答案
按热度按时间gmxoilav1#
静态Assert用于在编译时进行Assert。当静态Assert失败时,程序就不能编译。这在不同的情况下很有用,例如,如果你用代码实现某些功能,而这些代码依赖于
unsigned int
对象,而unsigned int
对象正好有32位。你可以这样放置一个静态Assert在另一个平台上,如果
unsigned int
类型的大小不同,则编译将失败,从而引起开发人员对代码中有问题的部分的注意,并建议他们重新实现或重新检查它。再举一个例子,您可能希望将某个整数值作为
void *
指针传递给某个函数(这是一个技巧,但有时很有用),并且您希望确保该整数值适合该指针您可能需要对
char
类型进行签名或者带有负值的整数除法向零舍入
如此等等。
运行时Assert在很多情况下可以用来代替静态Assert,但是运行时Assert只在运行时工作,并且只有当控制权传递给Assert时才起作用。因此,失败的运行时Assert可能处于休眠状态,在很长一段时间内都不会被检测到。
当然,静态Assert中的表达式必须是编译时常量,不能是运行时值,对于运行时值,你没有其他选择,只能使用普通的
assert
。uelo1irk2#
我突然想到...
假设
SomeLibrary::Version
被声明为静态常量,而不是#define
d(如C++库中所期望的)。与必须实际编译
SomeLibrary
和您的代码,链接所有内容,然后运行可执行文件相比,* 然后 * 才发现您花了30分钟编译了一个不兼容的SomeLibrary
版本。阿拉克,回应您的评论:是的,您可以将
static_assert
放在任何位置,从外观上看:第一次
lmvvr0a83#
我用它来确保我对编译器行为、头文件、库甚至我自己的代码的假设是正确的。例如,我在这里验证结构体是否被正确地打包到了预期的大小。
在 Package
stdio.h
的fseek()
的类中,我使用了一些enum Origin
的快捷方式,并检查这些快捷方式是否与stdio.h
定义的常量一致当行为是在编译时而不是在运行时定义的时候,你应该更喜欢
static_assert
而不是assert
,就像我上面给出的例子一样。BOOST_STATIC_ASSERT
是一个C ++0x之前的宏,如果条件不满足,它会生成非法代码。尽管static_assert
是标准化的,可以提供更好的编译器诊断,但目的是相同的。eagi6jfj4#
BOOST_STATIC_ASSERT
是static_assert
功能的跨平台 Package 器。目前我正在使用static_assert,以便在类上强制执行“概念”。
例如:
如果不满足上述任何条件,这将导致编译时错误。
cgh8pdjw5#
static_assert
的一个用途可能是确保一个结构(即与外界的接口,如网络或文件)的大小正好是你所期望的。这将捕捉到有人在没有意识到后果的情况下从结构中添加或修改成员的情况。static_assert
将拾取它并警告用户。ef1yzkbh6#
在没有概念的情况下,可以使用
static_assert
进行简单可读的编译时类型检查,例如,在模板中:az31mfrm7#
这并没有直接回答最初的问题,但是对如何在C++11之前强制执行这些编译时检查进行了有趣的研究。
Andrei Alexanderssu的Modern C++ Design的第2章(2.1节)实现了编译时Assert的思想,如下所示
比较宏STATIC_CHECK()和static_assert()
zkure5ic8#
要添加到所有其他答案上,它在使用非类型模板参数时也很有用。
请考虑以下示例。
假设你想定义一个函数,它的特定功能可以在编译时确定,比如下面的一个平凡函数,它返回一个在编译时确定的范围内的随机整数。但是,你想检查范围内的最小值是否小于最大值。
如果没有
static_assert
,您可以执行以下操作:如果是
min < max
,则一切正常,if constexpr
分支在编译时被拒绝。然而,如果是min >= max
,程序仍然可以编译,但现在你有一个函数,当它被调用时,将100%地抛出异常。因此,在后一种情况下,即使“错误”(min
大于或等于max
)在编译时出现,也只能在运行时发现。这就是
static_assert
的用武之地。因为
static_assert
是在编译时计算的,所以如果它测试的布尔常量表达式被计算为false,则会生成编译时错误,程序将无法编译。因此,上述功能可以改进为:
现在,如果用等于或大于
max
的min
值示例化函数模板,那么static_assert
将计算其布尔常量表达式为false,并将抛出编译时错误,从而立即警告您该错误,而不会在运行时出现异常。(Note:上述方法只是一个示例,不应用于生成随机数,因为通过
rand()
传递给std::mt19937
构造函数的种子值是相同的,因此快速连续地重复调用该函数将生成相同的数字。(由于time(nullptr)
返回相同的值)-而且,由std::uniform_int_distribution
生成的值的范围实际上是闭合区间,这样就可以将相同的值传递给其构造函数,以获得上界和下界(尽管不会有任何点))hfsqlsce9#
static_assert
可用于禁止使用delete
关键字,方法如下:#define delete static_assert(0, "The keyword \"delete\" is forbidden.");
如果每个现代C开发人员都希望使用保守的垃圾收集器(仅使用重载operator new的class和struct),那么他/她可能都希望这样做所述保守垃圾收集器可以通过调用在
main
的开始处执行此操作的某个函数来初始化和示例化得双曲余切值.例如,每一个想要使用Boehm-Demers-Weiser保守垃圾收集器的现代C开发人员都会在
main
函数的开头写上:GC_init();
在每个
class
和struct
中,以这种方式重载operator new
:现在不再需要
operator delete
,因为Boehm-Demers-Weiser保守垃圾收集器负责在不再需要时释放和解除分配每个内存块,开发人员希望禁止delete
关键字。一种方法是通过以下方式重载
delete operator
:但是不建议这样做,因为现代的C++开发人员会知道他/她在运行时错误地调用了
delete operator
,但是最好在编译时尽快知道这一点。因此,在我看来,这种情况的最佳解决方案是使用
static_assert
,如本答案开头所示。当然,这也可以用
BOOST_STATIC_ASSERT
来实现,但我认为static_assert
更好,应该总是首选。