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

问题描述 投票:10回答:3

正如std::chrono::duration::operator+=所述,签名是

duration& operator*=(const rep& rhs);

这让我很好奇。我会假设持续时间文字可以像任何其他内置文件一样使用,但事实并非如此。

#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;
}

输出是

150% of 10min: 10min
150% of 10: 15

为什么界面选择那种方式?在我看来,一个界面就像

template<typename T> 
duration& operator*=(const T& rhs);

会产生更直观的结果。

编辑: 感谢您的回复,我知道实现的行为方式以及我如何处理它。我的问题是,为什么这样设计。

我希望在操作结束时转换为int。在下面的示例中,两个操作数在乘法发生之前被提升为double。 4.5的中间结果之后转换为int,因此结果为4。

int i = 3;
i *= 1.5;
assert(i == 4);

我对std::duration的期望是它的行为方式相同。

c++ c++11 duration
3个回答
7
投票

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

它是以这种方式设计的(具有讽刺意味),因为基于积分的计算旨在提供精确的结果,或者不进行编译。但是在这种情况下,<chrono>库无法控制在绑定到参数之前将哪些转换应用于参数。

作为一个具体的例子,考虑将m初始化为11min的情况,并假设我们有一个模板化的operator*=,如你所示。确切的答案现在是16.5min,但基于积分的类型chrono::minutes不能代表这个值。

优越的设计是拥有这条线:

m *= 1.5f;  // compile-time error

不编译。这将使库更加自洽:基于积分的算法要么精确(或要求duration_cast),要么不编译。这可以实现,并且为什么没有这样做的答案仅仅是我没有想到它。


如果你(或其他任何人)对此感到非常强烈,试图将上述声明的编译时错误标准化,我愿意在委员会中赞成这样的提议。

这项工作将涉及:

  • 单元测试的实现。
  • 使用它来了解它将破坏多少代码,并确保它不会破坏无意图的代码。
  • 写一篇论文并将其提交给C ++委员会,目标是C ++ 23(目标C ++ 20为时已晚)。

最简单的方法是从开源实现开始,例如gcc的libstdc ++或llvm的libc ++。


10
投票

这里的问题是

auto m = 10min;

给你一个std::chrono::duration,其中rep是一个有符号整数类型。当你这样做

m *= 1.5f;

1.5f被转换为rep类型,这意味着它被截断为1,它在乘法后给出相同的值。

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

auto m = 10.0min;

得到std::chrono::duration使用浮点类型为rep并且当你做1.5f时不会截断m *= 1.5f;


3
投票

看看operator*=的实现:

_CONSTEXPR17 duration& operator*=(const _Rep& _Right)
    {   // multiply rep by _Right
    _MyRep *= _Right;
    return (*this);
    }

运营商采取const _Rep&。它来自std::duration,看起来像:

template<class _Rep, //<-
    class _Period>
    class duration
    {   // represents a time Duration
    //...

那么现在我们来看看std::chrono::minutes的定义:

using minutes = duration<int, ratio<60>>;

很明显,_Rep是一个int


因此,当你打电话给qazxsw poi时,qazxsw poi正被施放到operator*=(const _Rep& _Right) - 这等于1.5f,因此不会影响任何多重本身。

所以,你可以做什么?

你可以将它分成int并使用1m = m * 1.5f投射到std::chrono::duration_cast

std::chrono::duration<float, std::ratio>

150分钟10分钟:15分钟


如果你不喜欢总是投射它,使用std::chrono::duration<int, std::ratio>作为第一个模板参数:

m = std::chrono::duration_cast<std::chrono::minutes>(m * 1.5f);

甚至更快 - float为@NathanOliver回答:-)

© www.soinside.com 2019 - 2024. All rights reserved.