C++ 为什么行为std::chrono::duration::operator*=不像内置的*=?

C++ 为什么行为std::chrono::duration::operator*=不像内置的*=?,c++,c++11,duration,C++,C++11,Duration,如所述,签名为 duration& operator*=(const rep& rhs); 这让我感到奇怪。我假设duration文字可以像其他内置文字一样使用,但事实并非如此 #include <chrono> #include <iostream> int main() { using namespace std::chrono_literals; auto m = 10min; m *= 1.5f; std::co

如所述,签名为

duration& operator*=(const rep& rhs);
这让我感到奇怪。我假设duration文字可以像其他内置文字一样使用,但事实并非如此

#include <chrono>
#include <iostream>

int main()
{
    using namespace std::chrono_literals;
    auto m = 10min;
    m *= 1.5f;
    std::cout << " 150% of 10min: " << m.count() << "min" << std::endl;

    int i = 10;
    i *= 1.5f;
    std::cout << " 150% of 10: " << i << std::endl;
}
为什么界面是这样选择的?在我看来,像

template<typename T> 
duration& operator*=(const T& rhs);
我对std::duration的期望是它的行为方式相同。

这里的问题是

auto m = 10min;
提供一个
std::chrono::duration
,其中
rep
是有符号整数类型。当你这样做的时候

m *= 1.5f;
1.5f
被转换为类型
rep
,这意味着它被截断为
1
,这在乘法后为您提供相同的值

要解决此问题,您需要使用

auto m = 10.0min;

获取一个
std::chrono::duration
,它使用浮点类型表示
rep
,并且在执行
m*=1.5f时不会截断
1.5f

查看
运算符*=
的实现:

_CONSTEXPR17 duration& operator*=(const _Rep& _Right)
    {   // multiply rep by _Right
    _MyRep *= _Right;
    return (*this);
    }
操作员接受一个
常量代表&
。它来自
std::duration
,看起来像:

template<class _Rep, //<-
    class _Period>
    class duration
    {   // represents a time Duration
    //...
很明显,
\u Rep
是一个
int


因此,当您调用
操作符*=(const _Rep&_Right)
1.5f
将被转换为
int
,它等于
1
,因此不会影响任何自身的多应用程序

那你能做什么呢?

您可以将其拆分为
m=m*1.5f
并使用
std::chrono::duration\u cast
std::chrono::duration
std::chrono::duration

甚至更快-
autom=10.0分钟;m*=1.5f作为@NathanOliver回答:-)

我的问题是,为什么它是这样设计的

它是这样设计的(讽刺的是),因为基于积分的计算是为了给出精确的结果,或者不是为了编译。但是,在这种情况下,
库在绑定到参数之前无法控制将哪些转换应用到参数

作为一个具体的例子,考虑<代码> m <代码>初始化为<代码> 11min ,并假定我们有一个模板<代码>操作符*= < /C> >按您的建议。现在的确切答案是

16.5min
,但基于积分的类型
chrono::minutes
无法表示此值

一个更好的设计是采用以下产品线:

m *= 1.5f;  // compile-time error
不编译。这将使库更加自洽:基于整数的算法要么精确(要么需要
持续时间\u cast
),要么不编译。这是可以实现的,至于为什么不这样做,答案很简单,我没有想到


如果您(或任何其他人)对此有足够强烈的感受,试图将上述声明的编译时错误标准化,我愿意在委员会中支持这样的建议

这项工作将涉及:

  • 带有单元测试的实现
  • 部署它以了解它将破坏多少代码,并确保它不会破坏非预期的代码
  • 编写一篇论文并提交给C++委员会,以C++ 23为目标(现在对C++ 20来说太晚了)。

做到这一点最简单的方法是从开源实现开始,比如gcc的libstdc++或llvm的libc++。

rep是一种表示滴答数的算术类型,是至少29位*/
分钟的有符号整数类型,而不是
浮点
POD
在这里不是正确的术语。你的意思可能是“内置”。除了术语之外,我100%同意a
模板持续时间和运算符*=(const T&rhs)版本将更直观。例如,
inti=10;i*=1.5也导致了<代码> i==15 < /COD>,我想知道为什么“自定义类型应该像内置的C++一样”,这里不遵循“基于积分的算术是精确的(或需要持久化的)或不编译的。”(更多)与上面提供的内置示例一致,哪些符合,哪些不准确?编译错误不是提供了另一种不一致性吗?(我会考虑写一份提案。我对这个过程还不熟悉,所以可能需要一些时间来熟悉这个过程。:-)我在回答中将“一致”改为“自我一致”。也就是说,这一变化将使图书馆与自身更加一致。在其他算术表达式中,整数和浮点混合表达式的结果会产生基于浮点的结果。在chrono库中,如果没有明确的命名转换(例如,
duration\u cast
time\u point\u cast
,在C++17中,
floor
ceil
round
),基于浮点的类型将不会转换为基于整数的类型。@MichaelReinhardt 10min*1正好是10min。精度损失发生在调用操作符以满足签名之前。由于历史原因/继承自C),这不在chrono的控制范围之内。是的,如H.H.所述,它可以被固定(或工作),例如添加超载,即代码>删除的< /C> >,但是IMO C和C++应该最终删除潜在的有损隐式转换,以移除它们。是的,莱纳斯·托瓦尔兹(Linus Torvalds)会再次大肆抨击语言委员会的糟糕程度,但最终社区应该会变得更好。
template<class _Rep, //<-
    class _Period>
    class duration
    {   // represents a time Duration
    //...
using minutes = duration<int, ratio<60>>;
m = std::chrono::duration_cast<std::chrono::minutes>(m * 1.5f);
std::chrono::duration<float, std::ratio<60>> m = 10min;
m *= 1.5f; //> 15min
m *= 1.5f;  // compile-time error