Существовали как теоретические, так и практические проблемы, которые привели к принятию этого решения.
Теоретическая
Есть шутка, что человек с часами всегда знает, который час, а человек с двумя часами никогда не знает. В этой шутке есть доля правды, и это действительно повлияло на решение. Если приложению необходимо знать текущее время в двух или более местах и если часы имеют состояние, то существует проблема в том, чтобы один и тот же экземпляр часов использовался во всех местах, чтобы гарантировать, что все части кода имеют одно и то же определение «текущего времени».
Делая часы без сохранения состояния, но допуская несколько часов с разными типами, система типов может помочь программисту гарантировать, что программа использует одно и то же определение текущего времени в разных местах программы. И все же в тех случаях, когда требуется несколько определений времени, это также доступно, как отдельные типы.
Практическое
В качестве более практического соображения самыми первыми клиентами кода chrono::clock
были сами chrono
. Мы должны были есть нашу собачью еду. Возьмем для примера реализацию condition_variable::wait_until
:
https://github.com/llvm-mirror/libcxx/blob/master/include/__mutex_base#L377-L385
template <class _Clock, class _Duration>
cv_status
condition_variable::wait_until(unique_lock<mutex>& __lk,
const chrono::time_point<_Clock, _Duration>& __t)
{
using namespace chrono;
wait_for(__lk, __t - _Clock::now());
return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
}
Здесь функция берет один общий time_point
, и алгоритм должен найти текущее время, связанное с этим time_point
. Упаковка типа Clock
в тип time_point
и с наличием static now()
делает код очень чистым для написания и имеет очень чистый интерфейс. Тем не менее, достаточно универсально, что этот код будет работать с любыми пользовательскими часами, написанными пользователем: не только с часами, указанными в std.
Если бы часы были с состоянием, то либо:
condition_variable::wait_until
не удалось получить текущее время или
- Клиент должен будет передать часы, по которым также измеряется
time_point
.
Ни один из вышеперечисленных вариантов не показался мне приемлемым.
Обратите внимание, что condition_variable::wait_until
- это не особый случай, а всего лишь один пример из множества таких алгоритмов. Действительно, я предполагал, что такие алгоритмы будут писать не только разработчики стандартов, но и широкая публика. Вот пример последнего:
https://stackoverflow.com/a/35293183/576911
Да, я сталкивался со случаями, когда людям нужны часы с состоянием ОП этого вопроса предлагает такой пример. Но так как есть опция, что «часам с состоянием» все еще может быть присвоено статическое состояние, и если вам нужно другое состояние, используйте другой тип; И из-за преимуществ часов без гражданства, указанных выше; Выбор был сделан, что преимущество заключалось в дизайне часов без сохранения состояния.
Обновление
Я больше думал о клиенте, который говорит:
Так мои часы с состоянием не являются хорошим кодом?
Я думаю, что часы с состоянием - это хорошо, если только вы понимаете ограничения, которые на них установлены. И, к сожалению, я считаю, что это более сложная проблема, чем необходимо из-за незначительной ошибки в стандарте.
С практической точки зрения, есть только одна вещь, которую вы не можете сделать с часами с состоянием, которую вы можете сделать с часами без состояния, и это восходит к разделу выше под названием The Practical .
Нельзя создать экземпляр алгоритма (стандартного или иного), который требует, чтобы параметр шаблона был Clock
TM .
Например, если у вас time_point
на основе часов с состоянием, вы не можете использовать time_point
для вызова condition_variable::wait_until
. Если вы все равно не хотите этого делать, это не значит, что ваши часы с состоянием плохие. Если ваши часы с состоянием служат вашим целям, то это нормально.
В проекте C ++ 20 есть даже пример часов, которые не отвечают всем требованиям часов C ++ 11-17:
struct local_t {};
Да, это (вроде) часы. Но это почти ничего не делает. Он используется для создания семейства time_point
s, с которым не связано now()
:
template<class Duration>
using local_time = time_point<local_t, Duration>;
И это оказывается действительно полезным при различении моментов времени UTC от моментов времени, которые связаны с пока еще не определенным часовым поясом (подумайте о безопасности типа).
Итак, если создание часов без static now()
нормально для стандарта, почему это не так для вас ?! И единственная причина, о которой я могу подумать, заключается в том, что «незначительная ошибка» в стандарте, на который я ссылаюсь выше.
27.6 [time.point] в черновой спецификации C ++ 20 говорит это о template<class Clock, class Duration> class time_point
:
1 Clock
должен либо удовлетворять требованиям Cpp17Clock (27.3), либо быть типом local_t
.
Теперь я считаю, что это слишком ограничительно. Программисты должны иметь возможность создавать time_point
с часами с отслеживанием состояния. Они просто не могут позвонить condition_variable::wait_until
(и др.) С этим time_point
. Но они все еще могут получить все алгебраические преимущества от использования этих time_point
и duration
s, вытекающих из его различий.
Нет веских оснований для этого ограничения, за исключением стандартного изречения. И само существование local_t
, которое также не соответствует требованиям Cpp17Clock , в значительной степени подтверждает это.
1 Clock
должен либо удовлетворять требованиям Cpp17Clock (27.3), либо быть типом local_t
иметь вложенный тип duration
, если экземпляр по умолчанию имеет значение параметр шаблона Duration
.