这是我的问题。我有一个 BINARY_FLAG
宏:
#define BINARY_FLAG( n ) ( static_cast<DWORD>( 1 << ( n ) ) )
可以这样使用(“常量”场景):
static const SomeConstant = BINARY_FLAG( 5 );
或者像这样(“可变”场景):
for( int i = 0; i < 10; i++ ) {
DWORD flag = BINARY_FLAG( i );
// do something with the value
}
这个宏根本不是万无一失的 - 可以在那里传递 -1
或 34
最多会有一个警告,但行为将是未定义的。我想让它更简单。
对于常量场景,我可以使用模板:
template<int Shift> class BinaryFlag {
staticAssert( 0 <= Shift && Shift < sizeof( DWORD) * CHAR_BIT );
public:
static const DWORD FlagValue = static_cast<DWORD>( 1 << Shift );
};
#define BINARY_FLAG( n ) CBinaryFlag<n>::FlagValue
但这不适用于“变量”场景——我需要一个运行时断言:
inline DWORD ProduceBinaryFlag( int shift )
{
assert( 0 <= shift && shift < sizeof( DWORD) * CHAR_BIT );
return static_cast<DWORD>( 1 << shift );
}
#define BINARY_FLAG( n ) ProduceBinaryFlag(n)
后者很好,但没有编译时检查。当然,我希望尽可能进行编译时检查,否则进行运行时检查。在任何时候我都希望尽可能少的运行时开销,所以我不希望在可以进行编译时检查时进行函数调用(可能不会内联)。
我看到了this question , 但它看起来不像是同一个问题。
是否有某种构造允许根据作为标志编号传递的表达式是编译时常量还是变量而在两者之间交替?
最佳答案
这比你想象的要简单:)
让我们看看:
#include <cassert>
static inline int FLAG(int n) {
assert(n>=0 && n<32);
return 1<<n;
}
int test1(int n) {
return FLAG(n);
}
int test2() {
return FLAG(5);
}
我不使用 MSVC,但我使用 Mingw GCC 4.5 编译:
g++ -c -S -O3 08042.cpp
第一种方法的结果代码如下:
__Z5test1i:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl 8(%ebp), %ecx
cmpl $31, %ecx
ja L4
movl $1, %eax
sall %cl, %eax
leave
ret
L4:
movl $4, 8(%esp)
movl $LC0, 4(%esp)
movl $LC1, (%esp)
call __assert
.p2align 2,,3
第二个:
__Z5test2v:
pushl %ebp
movl %esp, %ebp
movl $32, %eax
leave
ret
看到了吗?编译器足够聪明,可以为你做这件事。不需要宏,不需要元编程,不需要 C++0x。就这么简单。
检查 MSVC 是否做同样的事情...但是看 - 编译器很容易评估常量表达式并删除未使用的条件分支。如果您想确定,请检查它。 . 但一般来说 - 相信你的工具。
关于c++ - 我的代码如何区分编译时常量和变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6939103/