Некоторый общий код, управляющий функциями, должен работать по-разному в зависимости от того, имеет функция возвращаемое значение или нет. Например, позаимствовав проблему из этого вопроса, скажем нам нужно написать функцию time_it
, которая принимает функцию и некоторые аргументы, запускает ее и печатает прошедшее время. Это может сделать следующий код:
#include <chrono>
#include <type_traits>
#include <cmath>
#include <iostream>
template<class Fn, typename ...Args>
auto time_it(Fn fn, Args &&...args) ->
typename std::enable_if<
!std::is_void<typename std::result_of<Fn(decltype(std::forward<Args>(args))...)>::type>::value,
typename std::result_of<Fn(decltype(std::forward<Args>(args))...)>::type>::type
{
const auto start = std::chrono::system_clock::now();
auto const res = fn(std::forward<Args>(args)...);
const auto end = std::chrono::system_clock::now();
std::cout << "elapsed " << (end - start).count() << std::endl;
return res;
}
template<class Fn, typename ...Args>
auto time_it(Fn fn, Args &&...args) ->
typename std::enable_if<
std::is_void<typename std::result_of<Fn(decltype(std::forward<Args>(args))...)>::type>::value,
void>::type
{
const auto start = std::chrono::system_clock::now();
fn(std::forward<Args>(args)...);
const auto end = std::chrono::system_clock::now();
std::cout << "elapsed " << (end - start).count() << std::endl;
}
int main()
{
time_it([](double x){return std::cos(x);}, 3.0);
time_it([](double x){}, 3.0);
}
Как видно, есть разница между случаями, когда функция возвращает значение или нет. В первом случае значение должно быть сохранено, прошедшее время распечатано, а значение возвращено; в последнем случае после печати прошедшего времени больше ничего делать не нужно.
Вопрос в том, как поступить в обоих случаях:
В приведенном выше коде используются
std::enable_if
иis_void
, но первый (громоздкий сам по себе) аргументis_void
повторяется как последний аргументenable_if
- это громоздко и запахи, особ. так как большая часть тела повторяется.Вышеупомянутый ответ обходит проблему, поскольку прошедшее время печатается как побочный продукт деструктора вызываемого класса некоторого истекшего таймера. Это хорошая идея, но в более сложных случаях это может привести к запутанному коду (существенная работа выполняется в деструкторе какого-то отдельного класса - это не естественный поток).
Есть ли лучший способ сделать это?