constexpr中的"整数常量溢出"警告

use*_*952 4 c++ constexpr c++11 visual-studio-2015

我正在尝试在编译时找到一个constexpr兼容的哈希函数用于散列字符串.字符串的数量非常小(<10)并且我对碰撞进行了单独检查,因此算法可能远非完美.我在互联网上找到了以下版本的FNV1A:

static constexpr unsigned int Fnv1aBasis = 0x811C9DC5;
static constexpr unsigned int Fnv1aPrime = 0x01000193;

constexpr unsigned int hashFnv1a(const char *s, unsigned int h = Fnv1aBasis)
{
    return !*s ? h : hashFnv1a(s + 1, (h ^ *s) * Fnv1aPrime);
}
Run Code Online (Sandbox Code Playgroud)

但是当我在MSVS 2015中编译它时,我得到以下警告:

warning C4307: '*': integral constant overflow
Run Code Online (Sandbox Code Playgroud)

由于函数中只有一个乘法我会假设警告来自(h ^ *s) * Fnv1aPrime.这是有道理的,因为乘以0x811C9DC5(Fnv1aBasis)几乎任何东西都会使32位整数溢出.

有什么方法可以解决这个问题吗?我已经尝试了几个其他constexpr函数,我发现它们用于散列字符串,但它们都有相同的问题.

Che*_*Alf 9

如果您不介意溢出,那么只需将警告静音.无符号整数运算保证是模2 n运算,其中n是值表示中的位数,因此无论如何都可以很好地定义.警告是一个愚蠢的警告; 它警告你,你正在使用无符号整数的主要特征.


我发现使用本地#pragma warning( disable: 4307 )功能时,每次使用该功能时都会出现警告.

此重写会使32位散列函数的警告静音:

constexpr auto hashFnv1a( char const* s, unsigned h = Fnv1aBasis )
    -> unsigned
{
    return !*s ? h : hashFnv1a(s + 1, static_cast<unsigned>( 1ULL*(h ^ *s) * Fnv1aPrime ));
}
Run Code Online (Sandbox Code Playgroud)

即使是广泛的谷歌搜索也没有找到任何方法来禁用关于无符号值溢出的愚蠢警告,同时将其保留为已签名的值,因此为了处理64位散列函数,似乎唯一的办法是实现constexpr64位无符号值乘法函数.因为它constexpr是否特别有效并不重要.所以:

#include <stdint.h>

namespace b32 {
    static constexpr uint32_t Fnv1aBasis = 0x811C9DC5u;
    static constexpr uint32_t Fnv1aPrime = 0x01000193u;

    constexpr auto hashFnv1a( char const* s, uint32_t h = Fnv1aBasis )
        -> uint32_t
    { return !*s ? h : hashFnv1a(s + 1, static_cast<uint32_t>( 1ULL*(h ^ *s)*Fnv1aPrime )); }
}  // namespace b32

namespace b64 {
    static constexpr uint64_t Fnv1aBasis = 0xCBF29CE484222325uLL;
    static constexpr uint64_t Fnv1aPrime = 0x100000001B3uLL;

    constexpr auto lo( uint64_t x )
        -> uint64_t
    { return x & uint32_t( -1 ); }

    constexpr auto hi( uint64_t x )
        -> uint64_t
    { return x >> 32; }

    constexpr auto mulu64( uint64_t a, uint64_t b )
        -> uint64_t
    {
        return 0
            + (lo( a )*lo( b ) & uint32_t(-1))
            +   (
                    (
                        (
                            (
                                (
                                    hi( lo( a )*lo( b ) ) +
                                    lo( a )*hi( b )
                                )
                                & uint32_t(-1)
                            )
                            + hi( a )*lo( b )
                        )
                        & uint32_t(-1)
                    )
                    << 32
                );
    }

    constexpr auto hashFnv1a( char const* s, uint64_t h = Fnv1aBasis )
        -> uint64_t
    { return !*s ? h : hashFnv1a( s + 1, mulu64( h ^ *s, Fnv1aPrime ) ); }
}  // namepace b64

#include <assert.h>
#include <iostream>
using namespace std;
auto main()
    -> int
{
    constexpr auto x = b64::mulu64( b64::Fnv1aBasis, b64::Fnv1aPrime );

    #ifdef _MSC_VER
    #   pragma warning( push )
    #   pragma warning( disable: 4307 )
        constexpr auto y = b64::Fnv1aBasis*b64::Fnv1aPrime;
    #   pragma warning( pop )
    #else
        constexpr auto y = b64::Fnv1aBasis*b64::Fnv1aPrime;
    #endif

    cout << x << endl;
    cout << y << endl;
    assert( x == y );

    static constexpr const char* const s = "blah!";
    constexpr unsigned xs = b32::hashFnv1a( s );
    constexpr uint64_t ys = b64::hashFnv1a( s );

    int a[1 + xs%2];  (void) a;
    int b[1 + ys%2];  (void) b;
}
Run Code Online (Sandbox Code Playgroud)


min*_*iot 4

您可以显式地转换为unsigned long long和返回,如下所示:

constexpr unsigned int hashFnv1b(const char *s, unsigned int h = Fnv1aBasis)
{
    return !*s
           ? h
           : hashFnv1b(
               s + 1,
               static_cast<unsigned int>(
                 (h ^ *s) * static_cast<unsigned long long>(Fnv1aPrime)));
}
Run Code Online (Sandbox Code Playgroud)

会停止我的现场演示中的警告(第 20 行触发它,第 21 行不会)。