为什么C++中numeric_limits<enum_type>::max()等于0?

14

以下是一段看起来似乎能够工作的代码:

#include <cassert>
#include <limits>

enum test { A = 1 };

int main()
{
    int max = std::numeric_limits<test>::max();
    assert(max > 0);
}

但是在Linux上,无论是使用GCC(4.6.2)还是clang(2.9),它都会失败:枚举类型的max()实际上为零!即使您使用C++11枚举类型限定符明确指定要使用的枚举类型,这种情况仍然如此。

为什么会这样?至于C++11的行为,是否有明确的调用?我在Strongly Typed Enums的N2347文件中找不到任何提及。


1
你期望这个值是多少? - James McNellis
1
is_specialized 的结果是什么? - RaptorFactor
@RaptorFactor: 不过,这并不是“test”对象可表示的最大值。 “test”对象可表示的最大值为1 - James McNellis
1
@JohnZwinck:您可以检查 is_specialized。如果不是真的,则应将所有值视为无意义。 - David Stone
请注意,在C++11中通常不能给出值为1,因为您可以前向声明您的enum。在使用std::numeric_limits时,编译器可能不知道枚举的任何值。 - David Stone
显示剩余6条评论
4个回答

29

std::numeric_limits在标准库中是专为每种算术类型——包括浮点数和整数,以及bool——进行特化的(§18.3.2.1/2)。

你的枚举类型test不属于这些类型之一,因此使用了基本模板。其行为由§18.3.2.3/1指定:“默认的numeric_limits<T>模板应该具有所有成员,但值为0或false”。

如果你想要了解test的底层类型的特性,可以使用underlying_type

std::numeric_limits<std::underlying_type<test>::type>::max()

另外,你可以为 test 特化 numeric_limits 并返回你想要的值。然而,这并不是一个特别好的主意。


1
@RobKennedy:这样做最好的情况下会很奇怪,最糟糕的情况下会误导人。你不能一次性为所有枚举类型进行专门化(因为标准库中有枚举类型),所以你必须为每个单独的枚举类型进行专门化。那么,值应该是什么呢?你不能直接推迟到underlying_type,因为底层类型可表示的最大值不一定是枚举类型可表示的最大值(例如,如果testint表示,则最大的int值可能是2^32-1,但最大的test值是1)。 - James McNellis
5
为什么有人会想要为所有枚举类型进行一次专门化?通常情况下,人们只对特定的“test”枚举类型感兴趣。这样做没有任何问题,对吧? - jrok
2
我不会指望能够依赖底层类型。如果可能的话,我们就不需要首先进行专门化了。进行专门化的人应该知道类型的属性,并相应地实现numeric_limits函数。如果有许多枚举类型,这将是繁琐的,但我不认为这很奇怪。似乎是一种描述枚举属性的统一结构。危险在于让枚举的定义与相应的numeric_limits专业化不同步。 - Rob Kennedy
@jrok:除了它很尴尬且可能会引起误解外?没有,这样做没有任何问题。 - James McNellis
2
@RobKennedy:如果你想获取有关枚举的特征,请编写一个enum_traits,不要定义主模板,并为单个枚举进行专门化。 - James McNellis
@JamesMcNellis 假设你在这里已经9年了。在模板中,将数字和枚举类型统一处理是有用的场景。 - Spencer

3

对于模板的非专业版本,max 返回 T()。由于您没有为您的 test 类型编写 numeric_limits 专业化,因此您将获得默认实现。


这看起来是正确的。这在哪里有记录?numeric_limits文档中似乎没有提到。 - nh2
1
查看max文档,@nh2。 - Rob Kennedy

1
从C++11起草稿:
在18.3.2.1中,关于numeric_limits:

非算术标准类型,比如复数(26.4.2),不应该有专门化。

而枚举类型不是算术标准类型。
因此,在非专门的模板中:
template<class T> class numeric_limits {
    public:
    [...]
    static constexpr bool is_specialized = false;
    static constexpr T max() noexcept { return T(); }
};

那就是,非专用的max()函数会返回该类型的默认初始化值,即0。

1
枚举确实不是算术标准类型,但这并不意味着它不是非算术标准类型。它根本就不是任何一种标准类型。 - Rob Kennedy
@RobKennedy - 哦!你说得对!在这种情况下,那段话毫无意义。但这并不改变主要观点:numeric_limits没有为枚举类型进行专门化处理。 - rodrigo

1

numeric_limits<T>是一个常规的类模板,它与编译器没有任何特殊的连接方式来了解用户定义的enum类型。如果您查看<limits>文件,它具有返回所有内容为零的默认模板定义,并且有一堆针对各个类型的特定类型的规范,返回正确的常量。

您可以通过自己提供numeric_limits<test>的规范来将您的enum“插入”到numeric_limits中。您可以从<limits>中复制int的规范,并进行修改以适应您的需求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接