9

我想编写一个模板,它返回可以表示给定数字的最小有符号整数类型。这是我的解决方案:

/**
 * Helper for IntTypeThatFits.
 * Template parameters indicate whether the given number fits into 8, 16 or 32
 * bits. If neither of them is true, it is assumed that it fits 64 bits.
 */
template <bool fits8, bool fits16, bool fits32>
struct IntTypeThatFitsHelper { };

// specializations for picking the right type
// these are all valid combinations of the flags
template<> struct IntTypeThatFitsHelper<true, true, true> { typedef int8_t Result; };
template<> struct IntTypeThatFitsHelper<false, true, true> { typedef int16_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, true> { typedef int32_t Result; };
template<> struct IntTypeThatFitsHelper<false, false, false> { typedef int64_t Result; };

/// Finds the smallest integer type that can represent the given number.
template <int64_t n>
struct IntTypeThatFits
{
    typedef typename IntTypeThatFitsHelper<
        (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
        (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
        (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min())
    >::Result Result;
};

但是,GCC 不接受此代码。我收到一个错误“由于数据类型 [-Werror=type-limits] 的范围有限,比较总是正确的”。为什么会这样?n 是一个有符号的 64 位整数,对于不同的 n 值,所有比较可能是真或假,还是我忽略了什么?

我会很高兴得到任何帮助。

编辑:我应该提到我正在使用 C++11。

4

5 回答 5

4

这是 gcc 的一个问题,模板代码中的警告可能令人沮丧。您可以更改警告或使用其他方法。

您可能知道,模板化代码被分析了两次:

  • 首次遇到时(解析)
  • 为给定类型/值实例化一次

这里的问题是,在实例化时,检查是微不足道的(是65的,int谢谢),编译器没有意识到这个警告并不适用于所有实例化:(对于我们这些努力的人来说,这确实非常令人沮丧有警告的无警告编译体验

您有 3 种可能性:

  • 停用此警告,或将其降级为非错误
  • 使用编译指示有选择地为此代码停用它
  • 以另一种格式重新编写代码,使其不再触发警告

请注意,有时第三种可能性涉及巨大的变化和更复杂的解决方案。我建议不要使用复杂的代码来摆脱毫无头绪的警告

编辑

一种可能的解决方法:

template <int64_t n>
struct IntTypeThatFits {
    static int64_t const max8 = std::numeric_limits<int8_t>::max();
    static int64_t const min8 = std::numeric_limits<int8_t>::min();

    static int64_t const max16 = std::numeric_limits<int16_t>::max();
    static int64_t const min16 = std::numeric_limits<int16_t>::min();

    static int64_t const max32 = std::numeric_limits<int32_t>::max();
    static int64_t const min32 = std::numeric_limits<int32_t>::min();

    typedef typename IntTypeThatFitsHelper<
        (n <= max8 ) && (n >= min8 ), 
        (n <= max16) && (n >= min16), 
        (n <= max32) && (n >= min32)
    >::Result Result;
};

...通过更改比较中使用的数据类型,它应该使编译器警告静音。我想显式转换 ( int64_t(std::numeric_limits<int8_t>::max())) 也可以,但我发现这更具可读性。

于 2012-05-03T15:54:26.583 回答
3

发生错误是因为您要求 GCC 使用-Werror=type-limits. 如果您进行过比较,由于给定数据类型的范围将始终为真,该警告-Wtype-limits会给您一个警告,例如:

uint8_t x;
if(x >= 0) { ... }  // always true, unsigned integers are non-negative
if(x >= 256) { ... }  // always false

int32_t x;
if(x < 9223372036854775808LL) { ... }  // always true

此警告有时可能很有用,但在包括此在内的许多情况下,它只是无用的迂腐,可以忽略。它通常是一个警告(作为 的一部分启用-Wextra,如果你使用它的话),但是使用-Werroror-Werror=type-limits时,GCC 会使其成为错误。

由于在这种情况下,它实际上并不表示您的代码存在潜在问题,因此只需使用 关闭警告,或者如果您不介意在编译器输出中看到这些警告,则-Wno-type-limits使其不是错误。Werror=no-type-limits

于 2012-05-03T15:45:55.343 回答
1
   typedef typename IntTypeThatFitsHelper<
        (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
        (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
        (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min())
    >::Result Result;

你不能在 C++ 中做到这一点(在 C++11 中你可以)——numeric_limits<int8_t>::max()不是编译时间常数。你在使用 C++11 吗?

顺便说一句,Boost 已经为您提供了这个:http: //www.boost.org/doc/libs/1_49_0/libs/integer/doc/html/boost_integer/integer.html

于 2012-05-03T15:42:19.770 回答
1

我认为问题所在的其他答案是错误的。我不相信这是一个过度渴望编译器的情况,但我相信这是一个编译器错误。此代码仍会触发警告:

template<int64_t n>
bool a() {
    return (n <= static_cast<int64_t>(std::numeric_limits<int8_t>::max()));
}

调用时a<500>();,但此代码不会:

template<int64_t n>
bool a() {
    return (n <= static_cast<int64_t>(127));
}

std::numeric_limits::max() 计算结果为 127。如果没有其他人这样做,我将在今天晚些时候为此提交一份 bugzilla 报告。

于 2012-05-03T16:24:45.487 回答
0

您会收到警告,因为对于一些template <int64_t n> struct IntTypeThatFits小的n(小于 2^32)的实例,由于编译时操作数的范围,一些比较总是正确的(原文如此!) 。

在这种情况下,这可能被视为虚假警告,因为您的代码依赖于它,OTOH 您明确要求使用或类似的命令行开关使其成为错误-Werror,您基本上得到了您在此处询问的内容。

于 2012-05-03T15:48:11.537 回答