形成自定义 std::chrono::durations 和 std::ratios 的最佳方法是什么?

What is the best way to form custom std::chrono::durations and std::ratios?

我正在阅读 ,它使用滑稽的持续时间单位 microfortnights 以令人难忘的方式说明了一个好点。

typedef std::ratio<756, 625> microfortnights;
std::chrono::duration<int, microfortnights> two_weeks(1000000);

我想到了这个问题:

If I really wanted to do this (more likely some other non-trivial duration such as the time available during a frame, or during N cycles of a processor), what is the best way to do this?

我知道 ratio<N, D> 将创建一个与 ND 的每个值关联的唯一类型。所以 ratio<4, 6>ratio<2, 3> 是不同的类型,即使它们代表相同的(减少的)分数。我是否总是需要进行数学计算以将转换因子简化为约化项?

这样写会更方便:

using microfortnights = std::chrono::duration<long, ratio<86400*14, 1000000>>;

而不是:

using microfortnights = std::chrono::duration<long, ratio<756, 625>>;

但那样的话,它们将是两种不同的类型,而不是相同的类型。第一个表达式更容易检查正确性。但是这个分数有很多表示,第二个可以说是规范的,因此更受欢迎。如果我的程序中有太多实际上代表相同单元的类型,那么这可能会导致不必要的模板代码膨胀。

为了简洁起见,下面我忽略了命名空间。 duration 在命名空间 std::chrono 中,ratio 在命名空间 std 中。

有两种好方法可以始终确保您的 ratio 减少到最低项,而无需自己进行算术运算。第一个很直接:

直接公式

如果你只想直接跳到microfortnights,而不必计算86,400*14/1,000,000的约化分数是756/625,只需在[之后添加::type =17=]:

using microfortnights = duration<long, ratio<86400*14, 1000000>::type>;

每个 ratio<N, D> 的嵌套 type 是另一个 ratio<Nr, Dr>,其中 Nr/Dr 是减少的分数 N/D。如果 N/D 已经归约,则 ratio<N, D>::typeratio<N, D> 是同一类型。事实上,如果我已经发现 756/625 是正确的减少分数,但只是偏执地认为它可能会进一步减少,我可以写:

using microfortnights = duration<long, ratio<756, 625>::type>;

因此,如果您对 ratio 的表达方式有任何疑问,或者只是不想为检查而烦恼,您可以随时将 ::type 附加到 ratio 键入只是为了确定。

冗长的公式

自定义持续时间单位经常作为系列的一部分出现。让整个系列都可用于您的代码通常很方便。例如 microfortnights 显然与 fortnights 相关,而 fortnights 又与 weeks 相关,后者源自 days,后者源自 hours(或来自 seconds(如果您愿意)。

通过一次建立一个单元的家庭,您不仅可以使用整个家庭,还可以通过尽可能简单的转换将一个家庭成员与另一个家庭成员联系起来,从而减少出错的机会。此外,使用 std::ratio_multiplystd::ratio_divide,而不是乘以文字,也意味着您不必在任何地方都插入 ::type 以确保将 ratio 保持在最低条款。

例如:

using days = duration<long, ratio_multiply<hours::period, ratio<24>>>;

ratio_multiplytypedef-name 乘法的结果 已经减少到最低项。 所以上面完全相同的类型:

using days = duration<long, ratio<86400>>;

您甚至可以在同一个翻译单元中同时拥有两个定义,并且不会出现重定义错误。无论如何你现在可以说:

using weeks           = duration<long, ratio_multiply<days::period,       ratio<7>>>;
using fortnights      = duration<long, ratio_multiply<weeks::period,      ratio<2>>>;
using microfortnights = duration<long, ratio_multiply<fortnights::period, micro>>;

我们最终得到了 microfortnightstypedef-name,与我们的 完全相同的类型直接制定,但通过一系列更简单的转换。我们仍然不必为将分数减少到最低项而烦恼,而且我们现在有几个有用的单位,而不仅仅是一个。

另请注意使用 std::micro 代替 std::ratio<1, 1000000>。这是另一个避免粗心错误的地方。很容易(至少对我而言)输错(和读错)零的数量。