您能否举一个例子static_assert(...)
(C ++ 11)轻松解决问题?
我对运行时很熟悉assert(...)
。我什么时候static_assert(...)
比常规更喜欢assert(...)
?
另外,在 boost
其中有一个叫BOOST_STATIC_ASSERT
,它和一样static_assert(...)
吗?
您能否举一个例子static_assert(...)
(C ++ 11)轻松解决问题?
我对运行时很熟悉assert(...)
。我什么时候static_assert(...)
比常规更喜欢assert(...)
?
另外,在 boost
其中有一个叫BOOST_STATIC_ASSERT
,它和一样static_assert(...)
吗?
Answers:
从我的头顶上...
#include "SomeLibrary.h"
static_assert(SomeLibrary::Version > 2,
"Old versions of SomeLibrary are missing the foo functionality. Cannot proceed!");
class UsingSomeLibrary {
// ...
};
假设将SomeLibrary::Version
其声明为静态const,而不是#define
d(就像在C ++库中所期望的那样)。
与必须实际编译SomeLibrary
代码,链接所有内容并仅运行可执行文件相反,然后才发现您花了30分钟的时间来编译不兼容的SomeLibrary
。
@Arak,回应您的评论:是的static_assert
,从外观上您可以坐在任何地方:
class Foo
{
public:
static const int bar = 3;
};
static_assert(Foo::bar > 4, "Foo::bar is too small :(");
int main()
{
return Foo::bar;
}
$ g ++ --std = c ++ 0x a.cpp a.cpp:7:错误:静态声明失败:“ Foo :: bar太小:(”
static_assert
非执行环境吗?这似乎是一个很好的例子:)
static_assert
。如果在程序运行之前不知道条件,请使用assert
。
静态断言用于在编译时进行断言。当静态断言失败时,程序将不会编译。这在不同情况下很有用,例如,如果您通过严格依赖unsigned int
具有32位对象的代码来实现某些功能。您可以像这样放置一个静态断言
static_assert(sizeof(unsigned int) * CHAR_BIT == 32);
在您的代码中。在另一个平台上,大小不同unsigned int
类型的编译将失败,从而引起开发人员对代码有问题的部分的注意,并建议他们重新实现或重新检查它。
再举一个例子,您可能想要传递一些整数值作为void *
指向函数的指针(hack,但有时很有用),并且想要确保整数值适合指针
int i;
static_assert(sizeof(void *) >= sizeof i);
foo((void *) i);
您可能想资产该char
类型已签名
static_assert(CHAR_MIN < 0);
或具有负值的整数除法四舍五入为零
static_assert(-5 / 2 == -2);
等等。
在许多情况下,可以使用运行时断言代替静态断言,但是运行时断言仅在运行时有效,并且仅在控制权移交给断言时起作用。因此,失败的运行时断言可能处于休眠状态,长时间未检测到。
当然,静态断言中的表达式必须是编译时常量。它不能是运行时值。对于运行时值,您别无选择,只能使用normal assert
。
static_assert
专门从C ++ 11 引用。我的static_assert
上面只是静态断言的一些抽象实现。(我个人在C代码中使用类似的东西)。我的回答是关于静态断言的一般用途及其与运行时断言的区别。
unsigned int
。该标准不能保证。类型的变量unsigned int
可以合法地占用32位内存,而其中16位未使用(因此宏UINT_MAX
将等于65535
)。因此,您描述第一个静态断言(“ 具有正好32位的unsigned int
对象”)的方式具有误导性。为了符合您的描述,该断言也应包括在内:。static_assert(UINT_MAX >= 0xFFFFFFFFu)
我用它来确保我对编译器行为,标头,库甚至我自己的代码的假设都是正确的。例如,在这里,我验证该结构是否已正确打包为预期的大小。
struct LogicalBlockAddress
{
#pragma pack(push, 1)
Uint32 logicalBlockNumber;
Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);
在类包装stdio.h
的中fseek()
,我使用了一些快捷方式,enum Origin
并检查这些快捷方式是否与由定义的常量对齐stdio.h
uint64_t BasicFile::seek(int64_t offset, enum Origin origin)
{
BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET);
你应该更喜欢static_assert
过assert
当行为是在编译时定义,而不是在运行时,如我上面给出的例子。一个例子,这是没有的情况下,将包括参数和返回代码检查。
BOOST_STATIC_ASSERT
是C ++ 0x之前的版本,如果不满足条件,则生成非法代码。意图是相同的,尽管static_assert
是标准化的,并且可以提供更好的编译器诊断。
BOOST_STATIC_ASSERT
是一个跨平台包装 static_assert
。
目前,我正在使用static_assert以便在类上实施“概念”。
例:
template <typename T, typename U>
struct Type
{
BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value);
BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer);
/* ... more code ... */
};
如果不满足以上任何条件,将导致编译时错误。
这并不能直接回答原始问题,而是对如何在C ++ 11之前执行这些编译时检查进行了有趣的研究。
Andrei Alexanderscu撰写的现代C ++设计的第2章(第2.1节)实现了这样的编译时断言的想法:
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
比较宏STATIC_CHECK()和static_assert()
STATIC_CHECK(0, COMPILATION_FAILED);
static_assert(0, "compilation failed");
该static_assert
可用于禁止使用的delete
这样的关键字:
#define delete static_assert(0, "The keyword \"delete\" is forbidden.");
如果每个现代C ++开发人员想使用保守的垃圾回收器,而只想使用使操作符new重载的类 es和struct来调用保守的垃圾回收器的保守堆上分配内存的函数,则他或她想使用保守的垃圾回收器可以通过调用一些在函数开头执行此操作的函数来初始化和实例化。main
例如,每个想要使用Boehm-Demers-Weiser保守垃圾收集器的现代C ++开发人员都将在main
函数开始时编写:
GC_init();
而在每一个class
和struct
过载operator new
是这样的:
void* operator new(size_t size)
{
return GC_malloc(size);
}
现在operator delete
不再需要该内存了,因为Boehm-Demers-Weiser保守的垃圾收集器负责在不再需要时释放和释放每个内存块,因此开发人员希望禁止delete
关键字。
一种方法是通过delete operator
这种方式重载:
void operator delete(void* ptr)
{
assert(0);
}
但是不建议这样做,因为现代C ++开发人员会知道他/她错误地调用了 delete operator
运行时,但是最好在编译时就知道这一点。
因此,我认为这种情况的最佳解决方案是使用 static_assert
此答案开头所示的。
当然,也可以使用来完成此操作BOOST_STATIC_ASSERT
,但是我认为这static_assert
更好,应该更优先选择。