Подтвердить что ты не робот

Каков наилучший способ создания пользовательских std:: chrono:: durations и std:: ratio?

Я читал этот отличный ответ, в котором использовалась единица времени комического времени microfortnights, чтобы продемонстрировать хороший момент незабываемым образом.

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

И возник вопрос:

Если бы я действительно хотел это сделать (скорее, некоторые другие нетривиальные продолжительность, такая как время, доступное во время кадра, или во время N циклов процессора), что это лучший способ сделать это?

Я знаю, что ratio<N, D> создаст уникальный тип, связанный с каждым значением N и D. Итак, 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>>;

Но тогда это будут два разных типа, а не один и тот же. Первое выражение легче проверить на правильность. Но есть много представлений этой фракции, а вторая, возможно, является канонической и, следовательно, предпочтительной. Если у меня слишком много типов, блуждающих по моей программе, которые на самом деле представляют одну и ту же единицу, то это может привести к ненужному раздуванию кода шаблона.

4b9b3361

Ответ 1

Ниже я игнорирую пространства имен в интересах краткости. duration находится в пространстве имен std::chrono, а ratio находится в пространстве имен std.

Есть два хороших способа всегда обеспечить, чтобы ваш ratio был сведен к самым низким условиям без необходимости выполнять арифметику самостоятельно. Первое довольно прямое:

Прямая формулировка

Если вы просто хотите перейти прямо к microfortnights, но без необходимости выяснять, что уменьшенная доля 86,400 * 14/1,000,000 составляет 756/625, просто добавьте ::type после ratio:

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

Вложенный type каждого ratio<N, D> является другим ratio<Nr, Dr>, где Nr/Dr - сокращенная дробь N/D. Если N/D уже уменьшено, то ratio<N, D>::type является тем же типом, что и ratio<N, D>. Действительно, если бы я уже понял, что 756/625 был правильной сокращенной долей, но был просто параноидальным, думая, что его можно было бы еще больше уменьшить, я мог бы написать:

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

Итак, если у вас есть сомнения в том, что ваш ratio выражен в младших терминах или просто не хочет, чтобы вас беспокоили проверки, вы всегда можете добавить ::type к типу ratio, чтобы быть уверенным.

Вербальная формулировка

Пользовательские единицы продолжительности времени часто появляются как часть семьи. И часто бывает удобно иметь всю семью для вашего кода. Например, microfortnights, очевидно, связано с fortnights, которое, в свою очередь, связано с weeks, которое получено из days, которое получено из hours (или из seconds, если вы предпочитаете).

Создавая свою семью по одной единице за раз, вы не только получаете всю доступную семью, но и уменьшаете вероятность ошибок, связывая одного члена семьи с другим с самым простым возможным преобразованием. Кроме того, использование std::ratio_multiply и std::ratio_divide вместо умножения литералов также означает, что вам не нужно вставлять ::type всюду, чтобы обеспечить сохранение ratio в самых низких терминах.

Например:

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

ratio_multiply является typedef-именем для результата умножения, уже приведенного к младшим. Таким образом, вышесказанное является тем же самым типом, что и:

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

И мы закончили с typedef-name для microfortnights, который является тем же самым типом, что и в нашей прямой формулировке, но через серию гораздо более простых преобразований. Нам все еще не нужно беспокоиться об уменьшении фракций до самых низких значений, и теперь у нас есть несколько полезных единиц, а не только один.

Также обратите внимание на использование std::micro вместо std::ratio<1, 1000000>. Это еще одно место, чтобы избежать неосторожных ошибок. Это так просто (по крайней мере для меня), чтобы ввести в заблуждение (и неправильно прочитать) количество нулей.