Шаблонный аргумент и вывод параметров std::function
Предположим, есть функция шаблона foo()
который принимает произвольное количество аргументов. Учитывая последний аргумент, всегда std::function
как мне реализовать foo()
шаблон показан ниже таким образом, что CbArgs
будет содержать это std::function
параметры?
template<typename... InArgs, typename... CbArgs = ???>
// ^^^^^^^^^^^^
void foo(InArgs... args) { ... }
Например, CbArgs
должно быть {int,int}
если вызывается так:
std::function<void(int,int)> cb;
foo(5, "hello", cb);
Моей первой идеей было:
template<typename... InArgs, typename... CbArgs>
void foo(InArgs... args, std::function<void(CbArgs...)>) { ... }
Но это не компилируется:
note: template argument deduction/substitution failed:
note: mismatched types ‘std::function<void(CbArgs ...)>’ and ‘int’
foo(5, "hello", cb);
Вопрос первый:
Почему это не компилируется? Почему вывод аргумента шаблона терпит неудачу?
В конце концов я придумал это решение:
template<typename... InArgs, typename... CbArgs>
void fooImpl(std::function<void(CbArgs...)>, InArgs... args) { ... }
template<typename... InArgs,
typename CbType = typename std::tuple_element_t<sizeof...(InArgs)-1, std::tuple<InArgs...>>>
void foo(InArgs... args)
{
fooImpl(CbType{}, args...);
}
Вот CbType
последний тип в InArgs
который std::function
, Тогда временный CbType
передается fooImpl()
где CbArgs
выводятся. Это работает, но выглядит уродливо для меня.
Вопрос второй:
Интересно, есть ли лучшее решение без двух функций и временного экземпляра CbType
?
1 ответ
Почему это не компилируется? Почему вывод аргумента шаблона терпит неудачу?
Когда пакет параметров не является последним параметром, он не может быть выведен. Сообщаем компилятору содержимое InArgs...
сделает ваш foo
определение работы:
template<typename... InArgs, typename... CbArgs>
void foo(InArgs..., std::function<void(CbArgs...)>) { }
int main()
{
std::function<void(int,int)> cb;
foo<int, const char*>(5, "hello", cb);
}
В качестве альтернативы, как вы обнаружили в вашем обходном пути, просто InArgs...
в конце и обновите свой foo
вызов:
template<typename... InArgs, typename... CbArgs>
void foo(std::function<void(CbArgs...)>, InArgs...) { }
int main()
{
std::function<void(int,int)> cb;
foo(cb, 5, "hello");
}
Интересно, есть ли лучшее решение без двух функций и временного экземпляра
CbType
?
Вот возможный способ избежать ненужного временного экземпляра, но используя тот же механизм для удержания CbArgs...
просто обернуть CbType
в пустой обертке, и передать это fooImpl
вместо.
template <typename T>
struct type_wrapper
{
using type = T;
};
template<typename... InArgs, typename... CbArgs>
void fooImpl(type_wrapper<std::function<void(CbArgs...)>>, InArgs&&...) { }
template<typename... InArgs,
typename CbType =
std::tuple_element_t<sizeof...(InArgs)-1,
std::tuple<std::remove_reference_t<InArgs>...>>>
void foo(InArgs&&... args)
{
fooImpl(type_wrapper<CbType>{}, std::forward<InArgs>(args)...);
}
Дополнительные улучшения:
typename
послеtypename CbType =
было ненужным - это было удалено.args...
должно быть идеально направленоfooImpl
сохранить свою категорию стоимости. И то и другоеfoo
а такжеfooImpl
должен взятьargs...
в качестве экспедиционной ссылки.
Обратите внимание, что есть предложение, которое облегчит работу с нетерминальными пакетами параметров: P0478R0 - " Вывод аргументов шаблона для нетерминальных пакетов параметров функций". Это сделало бы вашу оригинальную реализацию работать как задумано.