#c #c 11 #templates #generic-programming
#c #c 11 #шаблоны #универсальный-программирование
Вопрос:
У меня был простой класс секундомера, который в основном использовался std::chrono::steady_clock::now() - this->start_
в его реализации для измерения прошедшего времени. Я решил обновить его и сделать полностью универсальным, совместимым с std, не зависящим от конкретного типа часов, чтобы пользователь мог указать свой собственный тип часов. У меня также был шаблон stopwatch::operator DurationType() const
для удобства, например, для преобразования в определенный тип длительности:
std::chrono::milliseconds msecs = stopwatch;
Но есть одна проблема: я не могу избавиться от chrono
заголовка, поскольку я использую его duration_cast
.
У меня есть 2 идеи. Один из них работает, но не очень удобен, а другой вообще не работает и довольно уродлив.
// stopwatch.h
#pragma once
// #include <chrono>
namespace eld
{
template <typename ClockType,
typename DurationCaster>
class stopwatch
{
public:
using clock_type = ClockType;
using duration = typename clock_type::duration;
stopwatch()
: start_(clock_type::now())
{}
void reset()
{
start_ = clock_type::now();
}
typename clock_type::duration result() const
{
return clock_type::now() - start_;
}
template<typename Duration>
Duration result() const
{
// return std::chrono::duration_cast<Duration>(result()); // TODO: remove this
return DurationCaster()(Duration(), result());
}
template<typename Duration>
operator Duration() const
{
return result<Duration>();
}
// This is ugly and does not work
// template <typename Caster>
// auto operator()(Caster caster) -> decltype(Caster::operator()(typename clock_type::time_point))
// {
// return caster(result());
// }
private:
typename clock_type::time_point start_;
};
}
// dummy.cpp
#include <stopwatch/stopwatch.h>
#include <iostream>
#include <chrono>
#include <thread>
// this is inconvenient
struct DurationCaster
{
template<typename ToDuration, typename FromDuration>
constexpr ToDuration operator()(ToDuration, FromDuration fromDuration) const
{
return std::chrono::duration_cast<ToDuration>(fromDuration);
}
};
int main()
{
eld::stopwatch<std::chrono::steady_clock, DurationCaster> steadyWatch{};
std::this_thread::sleep_for(std::chrono::milliseconds(20));
std::chrono::nanoseconds nanoseconds = steadyWatch.result();
std::cout << nanoseconds.count() << std::endl;
std::chrono::milliseconds milliseconds = steadyWatch;
std::cout << milliseconds.count() << std::endl;
// This is ugly and does not work
// milliseconds = steadyWatch([](typename decltype(steadyWatch)::duration from)
// {
// return std::chrono::duration_cast<std::chrono::milliseconds>(from);
// });
return 0;
}
Хотя первый вариант работает, несмотря на его неудобства, я хочу знать, могу ли я избавиться от std::chrono::duration_cast
в моем заголовке более элегантным способом.
Или это так хорошо, как получается?
PS: Что не так с моим вторым подходом? Почему я не могу его скомпилировать?
Ответ №1:
Если вы хотите использовать первый подход, может быть, передать DurationCaster в качестве аргумента конструктору, тогда можно будет использовать функции, а не только функциональные объекты? А также вам не нужно создавать DurationCaster
каждый раз operator()
, сделайте это один раз.
Или вы можете использовать default DurationCaster
, который использует duration_cast
under the hood , define как typename DurationCaster = your_default_caster
О том, почему не работает вторая: как я вижу, здесь ошибка в decltype(Caster::operator()(typename clock_type::time_point))
том, operator
что вы называете статической функцией-членом, это неправильно.
Это должно сработать:
template <typename Caster>
auto operator()(Caster caster) -> typename std::result_of<Caster(typename clock_type::duration)>::type
{
return caster(result());
}
PS: я знаю result_of
, что он устарел, но вопрос касается cpp11