. Некоторые общие функции, управляющие кодом, должны работать по-разному в зависимости от того, имеет ли функция возвращаемое значение или нет. Например, заимствуя проблему от this question, скажем, что нам нужно написать функцию time_it
, функция принимает и аргументирует, запускает ее и печатает прошедшее время. Следующий код может это сделать:Избегайте повторения для SFINAE. Дифференцирование между типами пустот и непустых типов.
#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
- это является громоздким и smells, особенно. так как большая часть тела повторяется.Вышеупомянутый ответ обходит проблему, поскольку прошедшее время печатается как побочный продукт деструктора, вызываемого каким-либо классом прошедшего таймера. Это хорошая идея, но в более сложных целях это приведет к запутанному коду (значительная работа выполняется в деструкторе какого-то отдельного класса - это не естественный поток).
Есть ли лучший способ сделать это?
Очень общий рефакторинг - спасибо! –
@AmiTavory Изменил его, чтобы использовать 'std :: experimental :: optional', который можно заменить' boost :: optional', из-за проблем с безопасностью, которые я заметил. – Yakk