Шаблонный аргумент и вывод параметров 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... в качестве экспедиционной ссылки.

пример wandbox


Обратите внимание, что есть предложение, которое облегчит работу с нетерминальными пакетами параметров: P0478R0 - " Вывод аргументов шаблона для нетерминальных пакетов параметров функций". Это сделало бы вашу оригинальную реализацию работать как задумано.

Другие вопросы по тегам