C++ 计算编译时常量,同时防止整数常量溢出
c++ computing compile time constants while preventing integral constant overflow
我对元编程的语言功能有点陌生,我正在尝试用public static const variables
进行一个简单的class
,该将通过编译时常量设置其值:
我想实现的目标:我想计算的值是某个指数的幂,这些指数以转换为以2
为基数的位数的字节数来衡量。所有计算均以 2 为基数。
例子:
1 byte(s) = 8 bits: value = pow(2, 8) = 256; 2 byte(s) = 16 bits: value = pow(2, 16) = 65536 4 byte(s) = 32 bits: value = pow(2, 32) = 4294967296 8 byte(s) = 64 bits: value = pow(2, 64) = 18446744073709551616
我尝试编写一个函数来进行计算以计算所需的值,同时尝试使用constexpr
或const
,并且我尝试使用templates
.我想这样使用const function
、constexpr function
或function template
:
// constexpr function
constexpr std::uint64_t pow2( const std::uint32_t expInBytes, const std::uint32_t base = 2 ) {
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
return static_cast<std::uint64_t>( expInBits == 0 ? 1 : base * pow2( base, expInBits - 1 ) );
}
// or function template
template<std::uint32_t expInbytes>
constexpr std::uint64_t pow2() {
const std::uint32_t base = 2;
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
return (expInBits == 0 ? 1 : base * pow2<expInBytes-1>() );
}
template<>
constexpr std::uint64_t pow2<0>() {
return 0;
};
// template parameter T not used but needed to use the class as such:
// BitCombinations<>::static_member;
template<typename T = const std::uint32_t>
class BitCombinations {
public: // template // non template
static const std::uint64_t ONE_BYTE = pow2<1>(); // pow2( 1 );
static const std::uint64_t TWO_BYTES = pow2<2>(); // pow2( 2 );
static const std::uint64_t FOUR_BYTES = pow2<4>(); // pow2( 4 );
static const std::uint64_t EIGHT_BYTES = pow2<8>(); // pow2( 8 );
};
通过我的努力,我产生了各种编译时间,运行时错误等。最新的尝试我能够获得上述pow2<>()
的模板版本进行编译和运行,但是我没有得到正确的结果。
我不确定我的pow2
实现是错误的还是语法错误,或者我是否正确使用const
或constexpr
,在某些情况下,我一直从MS Visual Studio 2017 CE编译器获得integral constant overflow
作为编译时错误。
我一直在遵循pow2()
函数的这些模式:
- nullptr.me:C++11 constexpr:编译时计算exp
- prosepoetrycode.potterpcs.net : 一个简单的幂函数 (C++)
- cppreference.com : 数学::exp2
- reformatcode.com:整数的C ++功能,模板元编程
我似乎无法解决这个问题,也不知道还能尝试什么。
请注意,您的最后一个案例目前是不可能的。不能以 8 字节类型存储 2^64,最大值为 2^64 - 1。至少在主流架构上,不知道你使用的是哪一个。
我看到您的函数模板有两个问题。
-
您只将结果乘以
base
一次,但通过执行expInBytes - 1
将位数数减少 8。因此,您需要将其乘以八次:return (expInBits == 0 ? 1 : base * base * base * base * base * base * base * base * pow2<expInBytes-1>() );
-
0
的专用化返回 0,任何数字乘以 0 都是 0。 :)如果您认为您用expInBits == 0
处理了这种情况,请再想一想:expInBits
0
的唯一方法是expInBytes
为 0,但这不能在主模板中,因为您有专门的expInBytes
为 0!这意味着该分支永远不会被占用,它实际上没有任何效果。
你的函数有同样的问题在 1),此外你在递归时传递了错误的值(expInBits
而不是expInBytes
)并且顺序错误(base 排在最后)。
在我看来,循环更容易理解,更不容易出错:
constexpr std::uint64_t pow2(const std::uint32_t expInBytes, const std::uint32_t base = 2) {
const std::uint32_t expInBits = expInBytes * CHAR_BIT;
std::uint64_t result = 1;
for (std::uint32_t i = 0; i < expInBits; ++i)
result *= base;
return result;
}
在 Rakete111 的帮助下,他的积极反馈;当他指出一些错误时,我能够解决我的问题。作为回报,我能够实现我想要的一些相似之处。在编译时计算2^n
。
这是工作代码:
inline constexpr std::uint64_t powerOfBits( const std::uint64_t base, std::uint64_t const exponent ) {
return (exponent == 0) ? 1 : (base * powerOfBits( base, exponent - 1 ));
}
/*template<typename T = const std::uint32_t>*/
class BitCombinations {
public:
// Because I don't care for "magic numbers"
static const std::uint64_t binaryBase = std::uint64_t(2);
static const std::uint64_t eightBits = std::uint64_t( 8 );
static const std::uint64_t sixteenBits = std::uint64_t( 16 );
static const std::uint64_t thirtyTwoBits = std::uint64_t( 32 );
static const std::uint64_t sixtyFourBits = std::uint64_t( 64 );
// Now Generate Our Compile Time Constants
static const std::uint64_t ONE_BYTE = powerOfBits( binaryBase , eightBits ); //
static const std::uint64_t TWO_BYTES = powerOfBits( binaryBase , sixteenBits ); //
static const std::uint64_t FOUR_BYTES = powerOfBits( binaryBase , thirtyTwoBits ); //
// For 64bit int need to subtract 1 from the exponent otherwise you will have integral overflow
// To prevent this we just take 2^63, then in any output display we will have to append the
// string or characters `x 2` so that the user knows the value is double what they are seeing.
static const std::uint64_t EIGHT_BYTES = powerOfBits( binaryBase , sixtyFourBits - 1 );
};
int main() {
std::cout << BitCombinations::ONE_BYTE << std::endl;
std::cout << BitCombinations::TWO_BYTES << std::endl;
std::cout << BitCombinations::FOUR_BYTES << std::endl;
// Remember that 2^64 causes overflow: need to append characters to user.
std::cout << BitCombinations::EIGHT_BYTES << " x 2" << std::endl;
std::cout << std::endl;
std::cout << "nPress any key and enter to quite." << std::endl;
char q;
std::cin >> q;
return 0;
}
非常感谢您的帮助,并为我指明了正确的方向。我会接受你的回答。
- 为什么乘以常量有符号整数分数没有优化?
- 初始化不是整数的巨大常量多维数组的最佳方法是什么?
- 如何在满足常量表达式的同时将整数传递给指针,传递给 std::array<double、integer>?
- 有什么区别 - 常量 int x = 5000;和常量整数 x = 50'00;在C++?
- 常量整数变量和数字的不同类型推导
- 错误 C2864:'element::next':只能在类 (STRUCT) 中初始化静态常量整数数据成员
- C++整数常量表达式定义
- C++ 计算编译时常量,同时防止整数常量溢出
- 当我要修改指针到常量整数时,为什么我的编译器不显示错误
- 包含 comptime 断言的整数常量表达式宏
- 用于定义公共变量静态常量整数的C++标准
- 将非常量整数从 in C++传递给模板参数
- 从文字字符串生成编译时常量整数
- 当我们在 c++ 中将常量整数引用分配给整数指针时,行为是什么
- 为什么允许常量整数指针指向非常量整数
- 存储在静态常量整数 (C++) 中的对数函数的错误返回值
- 常量整数意外更改C++
- 常量整数提升规则
- 为什么不通过获取内联定义的静态常量整数成员变量的地址来违反 ODR?
- 简单类中的常量整数特性