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