Лично я не вижу способа избавиться от перегрузок, если вы не знаете тип возвращаемого значения.Вы могли бы предположить, что тип возврата void
наиболее распространенный, и к этому тогда: (я упрощаю ваш пример для краткости)
template <class F, class... Args>
auto async(F f, Args... args)
{
return f(args...);
}
template <class... Args>
auto async(void (*f)(Args...), Args... args)
{
return f(args...);
}
void run();
void run(int, double);
auto test()
{
async(run); // calls run();
async(run, 1, 2.); // calls run(int, double);
}
Это действительно кажется подозрительным и запутанным для пользователя.Почему это работает, когда переданная функция возвращает void
, и не работает, если возвращает int
?Поэтому я не рекомендую это.
Так что единственное, что вы можете сделать, это дать пользователю возможность разобраться в этом.
Итак, некоторые решения для вызывающего абонента.function:
Старый (и уродливый) старый способ: используйте cast для устранения неоднозначности перегрузки:
async(static_cast<int(*)(int, double)>(run), 1, 2.);
Мне лично этот подход вообще не нравится.Мне не нравится его многословие, и больше всего мне не нравится, что я должен открыто говорить о чем-то, что действительно должно быть неявным.
Лямбда-путь
async([] { return run(1, 2.); });
Мне это нравится.Это не наполовину плохо.Все еще немного многословно, но намного лучше, чем другие альтернативы.
Макро способ
Да, макросы в C ++.Без дальнейших церемоний, это так (совершенная пересылка для краткости опущена):
#define OVERLOAD(foo) [] (auto... args) { return foo(args...); }
async(OVERLOAD(run), 1, 2.);
Я не собираюсь комментировать это.Я оставляю каждого из вас судить этот макрос.