c++计算编译时间常数,同时防止整数常数溢出

标签 c++ templates c++14 c++17 constexpr

我对元编程的语言特性有点陌生,我正在尝试制作一个简单的 classpublic static const variables将通过编译时间常量设置其值:

我想要实现的目标:我想计算一些指数的幂值,这些指数以字节数转换为基数为 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 functionfunction 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是错误的,或者我的语法错误,或者我没有使用 constconstexpr正确,在某些情况下我一直得到 integral constant overflow作为 MS Visual Studio 2017 CE 编译器的编译时错误。

我一直在为 pow2() 遵循这些模式功能:

我似乎无法全神贯注于此,也不知道还能尝试什么。

最佳答案

请注意,您的最后一个案例目前无法实现。 8字节的类型不能存2^64,最大是2^64 - 1,至少在主流架构上,不知道你用的是哪个。

我发现您的函数模板有两个问题。

  1. 您仅将结果与 base 相乘一次,但通过执行 expInBytes - 1 将位数减 8。所以,你需要将它乘以八次:

    return (expInBits == 0 ? 1 : base * base * base * base * base * base * base * base * pow2<expInBytes-1>() );
    
  2. 0 的特化返回 0,任何乘以 0 的数字都是 0。:) 如果您认为您用 expInBits == 0,再想一想:expInBits0 的唯一方法是 expInBytes 为 0,但它不能在主模板中,因为你当 expInBytes 为 0 时有专门化!这意味着该分支永远不会被采用,它实际上没有任何效果。

您的函数与 1) 中描述的问题相同,此外,您在递归时向它传递了错误的值(expInBits 而不是 expInBytes)和顺序是错误的(基数排在最后)。

在我看来,循环更容易理解并且不易出错:

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;
}

关于c++计算编译时间常数,同时防止整数常数溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48089550/

相关文章:

c++ - 预处理器和模板参数或代码段的条件编译

c++ - 为什么这个模板变量会导致编译器警告?

c++ - 从C++派生类的构造函数修改基类的数据成员

c++ - enable_if'ed 模板化模板构造函数的类型签名?

c++ - 奇怪的多线程问题

C++ 模板化函数调用托管函数

c++ - 如何在现代 C++ 中实现经典的排序算法?

c++ - 字符串以某个字符串开头

c++ - 程序无法启动,因为您的计算机缺少 mfc120ud.dll

c++ - 具有可变高阶函数的重载分辨率