为什么std::numeric_limits<seconds>::max()返回0?

20

我发现了一个有趣的陷阱,std::numeric_limits<seconds>::max()返回0。正确的做法是使用seconds::max()std::numeric_limits<seconds::rep>::max(),但我很想知道为什么会出现这种情况。我本来期望它要么在编译时失败,要么就正常工作。以下代码演示了在gcc 4.9.3中遇到的问题。

#include <iostream>
#include <limits>
#include <chrono> 

using namespace std;
using namespace std::chrono;

int main(int /*argc*/, const char* /*argv*/[])
{
    const auto maxSeconds = std::numeric_limits<seconds>::max();
    std::cerr << maxSeconds.count() << "\n";
    const auto maxSeconds2 = seconds::max();
    std::cerr << maxSeconds2.count() << "\n";
   return 0;
}

我在chrono头文件中没有看到任何隐式转换。如果一个duration被隐式转换为数值类型并且符号丢失或者是一个bool,那么你可能会得到最小值零,但是最大值零没有意义。
正如TartanLlama指出的,缺省特化使用默认构造函数,因此返回值为0。
深入研究一下旧版的标准文档standard,我发现以下规定:

18.3.2.3 类模板numeric_limits [numeric.limits]

非算术标准类型,例如complex<T>(26.4.2),不应有专门化。

稍后又有:

默认numeric_limits<T>模板将具有所有成员,但其值为0或false。

类型cv T的专门化的每个成员的值应与未限定类型T的专门化的相应成员的值相等。

缺失的是解释委员会为什么认为这比编译错误更好的说明。是否需要提交库缺陷报告?
更新:我已经向ISO委员会提出了这个问题。 https://issues.isocpp.org/show_bug.cgi?id=186

1
可能是为什么C++的numeric_limits<enum_type>::max()等于0?的重复问题。 - awesoon
这是一个重复的问题。我错过了那个。然而,到目前为止,对于这个问题最好的答案与这个问题的答案相同:“因为标准规定如此”。我正在寻找更多的见解。为什么标准要这样规定? - Bruce Adams
1
我也在寻找这种设计的原因,但不幸的是没有结果。你的问题不再是重复的了,我已经撤回了我的关闭投票。 - awesoon
我认为这是对语言特性的另一种陈旧的未充分利用。 (例如https://dev59.com/RGEh5IYBdhLWcg3w3mqK)也许你应该将`std::numeric_limits <T> :: max()替换为std::numeric_limits <T> :: is_specialized :: value?std::numeric_limits <T> :: max():throw 0; '(或某些聪明的编译时等效物)。 - alfC
顺便说一下,std::numeric_limits<T>::max() 应默认为 T::max(),如果不存在,则应该是编译器错误。(而且不要让我们开始谈论 std::numeric_limits<T>::min(),它对于浮点和整数类型来说是一个不一致的混乱。) - alfC
2个回答

11

std::numeric_limits 没有为 std::chrono::seconds 进行专门定义。对于未特化的类型,std::numeric_limits 中的所有数据成员和函数都提供了默认定义以避免编译器错误。在这种情况下,numeric_limits<T>::max() 的默认版本只返回 T(),即 0

您可以通过检查 std::numeric_limits<T>::is_specialized 是否为 false 来在编译时检查是否已为给定的 T 专门定义了 std::numeric_limits


2
我现在在“limits”头文件中看到它了。但是为什么numeric_limits<T>::max()的默认版本使用默认构造函数呢?这里肯定最好有一个编译器错误吧? - Bruce Adams
1
@BruceAdams老实说,我不确定。我本以为将其省略并让用户使用SFINAE会更好,但也许我漏掉了什么。 - TartanLlama
你和我一样。我会保持问题的开放,等待语言专家的回答,如果足够长时间后没有人出现,我会考虑提交一个库缺陷报告。 - Bruce Adams
3
@Bruce - <limits>头文件可以追溯到SFINAE不像今天这样流行的时代。而且如果没有if constexpr,两条路径都能编译通过通常是一种优势。 - Bo Persson
@Bo 听起来很有道理。也许你能演示一个这将会有用的情况?我很难想象。 - Bruce Adams

11

std::chrono::seconds本身不是标准算术类型,因此未为其专门设置std::numeric_limits。 因此,您只会看到一些相当无用的默认值。

要查询用于计算滴答声的基础类型的范围(在gcc下是64位的long int),请使用:

std::numeric_limits<seconds::rep>::max();

相反。


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