Передать указатель на функцию-шаблон Variadic - PullRequest
0 голосов
/ 07 апреля 2019

У меня есть функция printint, которая принимает целое число в качестве параметра.Как я могу передать его в шаблон Variadic, как показано ниже?Я попытался запустить run_callback (printint, 1), но он говорит "нет совпадения экземпляра с функцией".Пожалуйста, помогите, спасибо!

void printint(int i)
{

}

template <typename ...Args>
void run_callback(const std::function<void(Args...)>& func, Args ...as)
{
....
}

1 Ответ

4 голосов
/ 07 апреля 2019

Когда вы вызываете шаблон вашей функции, например, вот так

run_callback(printint, 1);

, компилятор попытается вывести аргументы шаблона, которые при замене на типы параметров функции приведут типы параметров функции к типамаргументов.Проблема здесь в том, что нет Args..., который вы могли бы вставить в const std::function<void(Args...)>&, чтобы этот тип соответствовал типу printint, который равен void(int).Следовательно, вывод аргумента шаблона завершается неудачей.

Если вы просто хотите, чтобы ваш run_callback использовал любую функцию с сигнатурой, совпадающей с Args..., попросите ее обратиться к функции с такой сигнатурой:

template <typename... Args>
void run_callback(void (&func)(Args...), Args... as)
{
}

живой пример

Это, однако, будет несколько хрупким, поскольку в основном требуется, чтобы типы в Args были точными совпадениями для типов параметров обратного вызова.Скорее всего, вы бы хотели, чтобы run_callback работал с любой функцией (или, в более общем смысле, с любой вызываемой функцией), которая может быть вызвана с указанным args.Один из способов добиться этого - сделать так, чтобы ваш шаблон функции принимал любой тип в качестве обратного вызова, но включал перегрузку только тогда, когда параметр func на самом деле вызывается и вызывается с использованием соответствующего списка аргументов:

template <typename F, typename... Args>
auto run_callback(F&& f, Args&&... as) -> std::enable_if_t<std::is_invocable_v<F, Args...>>
{
    f(std::forward<Args>(as)...);  // call the callback
}

живой пример

Наконец, если по какой-то причине вам действительно нужен параметр func, то есть std::function, вы можете скрыть пакет параметров Args... в func в не выведенном контексте :

template <typename... Args>
void run_callback(const std::common_type_t<std::function<void(Args...)>>& func, Args... as)
{
}

живой пример

Хитрость здесь заключается в использовании std::common_type_t<T>, которыйна самом деле это просто сокращение для std::common_type<T>::type, которое в итоге просто снова станет T (Примечание: в std::common_type нет ничего особенного, за исключением того, что он уже есть для нас; мы могли бы использовать любого другого помощникашаблон; все, что нам нужно, это то, что передает свой аргумент зависимому имени).В общем, невозможно однозначно определить, какой T вам нужно подключить к C<T>::xyz, чтобы C<T>::xyz стал определенным типом.Даже не гарантируется, что существует xyz для каждого T или что C<T>::xyz будет типом для начала.По этой причине спецификатор вложенного имени в квалифицированном-идентификаторе определен в качестве невведенного контекста [temp.deduct.type] /5.1.Короче говоря, это означает, что Args... теперь появляется в типе параметра func таким образом, что компилятор не будет пытаться определить, что Args... должно быть из аргумента, переданного для параметра func (т.е. он не будет пытаться сделать то, что привело к сбою вашего исходного кода).Однако он все еще может вывести Args... из пакета функциональных параметров as.Таким образом, дедукция типов просто сделает Args... теми типами аргументов, которые переданы для as..., и будут успешными.После успешного вывода типа выведенные Args... подставляются обратно в остальные параметры, и тип func получится равным const std::function<void(Args...)>&, но теперь с типами Args..., взятыми из того, из чего они были выведены, изпакет параметров as.

Другим способом сделать то же самое было бы, например, обернуть аргумент для первого параметра в списке инициализатора:

run_callback({ printint }, 1);

live example

Аргумент, который является списком инициализатора, также делает параметр не выводимым контекстом в этом случае [temp.deduct.type] /5.6, поэтому объяснениепочему это работает в основном так же, как в предыдущем примере.Обратите внимание, что, хотя предыдущий подход решает проблему из объявления шаблона, этот подход решает ее на месте вызова функции.

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