C++17 STL Стандартная библиотека шаблонов — страница 79 из 119

std::ratio<1, 1000>
(
std::ratio
— это дробное значение
a/b
). Тип
ratio_multiply
, по сути, является функцией времени компиляции, которая представляет собой тип, получаемый в результате умножения одного типа
ratio
на другой.

Это может показаться непонятным, так что рассмотрим пример: команда

ratio_multiply, ratio<4, 5>>
даст результат
ratio<8, 15>
, поскольку
(2/3) * (4/5) = 8/15
.

Полученные описания типов эквивалентны следующим описаниям:


using seconds      = chrono::durationratio<1, 1>>;

using milliseconds = chrono::durationratio<1, 1000>>;

using microseconds = chrono::duration>;


После получения этих типов можно легко выполнять преобразования между ними. При наличии промежутка времени

d
с типом
seconds
можно преобразовать его в тип
milliseconds
, передав в конструктор другого типа —
milliseconds(d)
.


Дополнительная информация

В других учебниках и книгах при преобразовании промежутков времени вы могли столкнуться с

duration_cast
. Если у нас есть промежуток времени типа
chrono::milliseconds
и нужно преобразовать его к типу
chrono::hours
, например, то следует написать конструкцию
duration_cast(milliseconds_value)
, поскольку данные единицы измерения зависят от целочисленных типов. Преобразование точных единиц времени в менее точные приводит к потере точности, именно поэтому и нужен
duration_cast
. Для продолжительностей, основанных на типах
double
или
float
, этого не требуется.

Выполняем преобразование между абсолютными и относительными значениями с использованием std::chrono

До C++11 было довольно сложно получить физическое время и просто вывести его на экран, поскольку C++ не имела собственной библиотеки для работы с временем. Требовалось всегда вызывать функции библиотеки С, которая выглядит очень архаично, учитывая, что такие вызовы могут быть инкапсулированы в собственные классы.

Начиная с C++11, в STL можно найти библиотеку

chrono
, она значительно упрощает решение задач, связанных с временем.

В этом примере мы возьмем местное время, выведем его на экран и поработаем с ним, добавляя разные смещения, что очень удобно делать с помощью библиотеки

std::chrono
.


Как это делается

В примере мы сохраним текущее время и выведем его на экран. Кроме того, наша программа будет добавлять разные смещения к сохраненному времени и выводить на экран полученные результаты.


1. Сначала идут типичные директивы

include
, затем мы объявляем об использовании по умолчанию пространства имен
std
:


#include 

#include 

#include 


using namespace std;


2. Выведем на экран абсолютные моменты времени. Они будут иметь форму шаблона типа

chrono::time_point
, поэтому просто перегрузим для него оператор выходного потока. Существуют разные способы вывести на экран дату и/или время для заданного момента. Мы применим только стандартное форматирование
%c
. Можно было бы, конечно, также вывести только время, только дату, только год или что-то еще, приходящее на ум. Все преобразования между разными типами до того, как мы сможем использовать
put_time
, будут выглядеть несколько «неаккуратно», но мы провернем это лишь однажды.


ostream& operator<<(ostream &os,

         const chrono::time_point&t)

{

  const auto tt (chrono::system_clock::to_time_t(t));

  const auto loct (std::localtime(&tt));

  return os << put_time(loct, "%c");

}


3. Для секунд, минут, часов и т.д. в STL существуют описания типов. Сейчас мы добавим тип

days
. Это делается легко; нужно лишь специализировать шаблон
chrono::duration
, сославшись на часы и умножив их на 24, поскольку сутки насчитывают 24 часа.


using days = chrono::duration<

  chrono::hours::rep,

  ratio_multiply>>;


4. Чтобы наиболее элегантным способом выразить продолжительность длиной в несколько дней, можно определить собственный пользовательский литерал

days
. Теперь можно написать
3_days
, чтобы создать значение, которое представляет собой три дня.


constexpr days operator ""_days(unsigned long long h)

{

  return days{h};

}


5. В самой программе сделаем снимок момента времени, который затем просто выведем на экран. Это очень легко и удобно, поскольку мы уже реализовали правильную версию перегруженного оператора.


int main()

{

  auto now (chrono::system_clock::now());

  cout << "The current date and time is " << now << '\n';


6. Сохранив текущее время в переменной

now
, можем добавить к нему произвольные продолжительности и также вывести их на экран. Добавим к текущему времени 12 часов и выведем результат на экран:


  chrono::hours chrono_12h {12};

  cout << "In 12 hours, it will be "

<< (now + chrono_12h)<< '\n';


7. Объявляя об использовании по умолчанию пространства имен

chrono_literals
, разблокируем все существующие литералы, описывающие продолжительность, для часов, секунд и т.д. Таким образом, можно изящно вывести на экран, какое время было 12 часов 15 минут назад или семь дней назад.


  using namespace chrono_literals;

  cout << "12 hours and 15 minutes ago, it was "

<< (now - 12h - 15min) << '\n'

<< "1 week ago, it was "

<< (now - 7_days) << '\n';

}


8. Компиляция и запуск программы дадут следующий результат. Поскольку мы использовали в качестве строки форматирования

%c
, получим довольно полное описание в конкретном формате. Поработав с разными строками формата, можем вывести время в любом формате, который нам нравится. Обратите внимание: здесь мы применяем 24-часовой формат.


$ ./relative_absolute_times

The current date and time is Fri May 5 13:20:38 2017

In 12 hours, it will be Sat May 6 01:20:38 2017

12 hours and 15 minutes ago, it was Fri May 5 01:05:38 2017

1 week ago, it was Fri Apr 28 13:20:38 2017


Как это работает

Мы получили текущий момент времени из

std::chrono::system_clock
. Этот класс часов STL единственный способен преобразовывать свои значения моментов времени в структуру
time
, которая может быть отображена в виде понятной человеку строки описания.

Чтобы вывести на экран такие моменты времени, мы реализовали оператор

<<
для потока вывода:


ostream& operator<<(ostream &os,

       const chrono::time_point&t)

{

  const auto tt (chrono::system_clock::to_time_t(t));

  const auto loct (std::localtime(&tt));

  return os << put_time(loct, "%c");

}


Здесь мы сначала преобразуем экземпляр типа

chrono::time_point
к типу
std::time_t
. Значения этого типа можно преобразовать в локальное время, что мы делаем с помощью функции