c ++计算编译时常量,同时防止积分常数溢出

时间:2018-01-04 06:03:32

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

我对元编程的语言功能有点新意,我试图用class创建一个简单的public 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

我尝试编写一个函数来进行计算,以便在尝试使用constexprconst时计算所需的值,并且我尝试使用templates。我想这样使用const functionconstexpr 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,在某些情况下我是从MS Visual Studio 2017 CE编译器继续获取integral constant overflow编译时错误。

我一直在关注pow2()函数的这些模式:

我似乎无法围绕这一点思考,不知道还有什么可以尝试。

2 个答案:

答案 0 :(得分:1)

请注意,目前无法使用您的最后一个案例。您不能以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时有专门化!这意味着永远不会采取该分支,它实际上没有效果。

  3. 你的函数有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;
    }
    

答案 1 :(得分:0)

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

非常感谢你的帮助,并指出了我正确的方向。我会接受你的回答。