Перегрузка функции вывода в шаблонной функции - PullRequest
0 голосов
/ 03 октября 2018

Я пишу свой собственный аналог std :: async (должен работать обратно в Intel13 / gcc 4.4 STL), и это прекрасно работает:

template <typename Func, typename ...Args>
struct return_value {
    template <typename T>
    using decayed = typename std::decay<T>::type;
    using type    = typename std::result_of<decayed<Func>(decayed<Args>...)>::type;
};

template <typename Func, typename ...Args>
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
    return func(args...);
}

void run(int a, double b) { 
    printf("a: %i  b: %f\n", a, b);
}

int main() {
    async(run, 1, 3.14);
}

Но если я добавлю перегрузку для run:

void run() {
    printf("no args\n");
}

Тогда он не сможет правильно разрешить:

<source>: In function 'int main()':
<source>:27:23: error: no matching function for call to 'async(<unresolved overloaded function type>, int, double)'
     async(run, 1, 3.14);
                       ^
<source>:14:43: note: candidate: 'template<class Func, class ... Args> typename return_value<Func, Args>::type async(Func&&, Args&& ...)'
 typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
                                           ^~~~~
<source>:14:43: note:   template argument deduction/substitution failed:
<source>:27:23: note:   couldn't deduce template parameter 'Func'
     async(run, 1, 3.14);
                       ^
Compiler returned: 1

Как я могу взять функцию в качестве параметра шаблона и правильно вывести перегрузку с учетом аргументов?

1 Ответ

0 голосов
/ 03 октября 2018

Лично я не вижу способа избавиться от перегрузок, если вы не знаете тип возвращаемого значения.Вы могли бы предположить, что тип возврата 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.);

Я не собираюсь комментировать это.Я оставляю каждого из вас судить этот макрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...