Lambda выводится в std :: function, если шаблон не имеет вариационных аргументов

template<typename ReturnT, typename... ParamT>
void foo(std::function<ReturnT(ParamT...)> callback)
{}

template<typename ReturnT, typename ParamT>
void bar(std::function<ReturnT(ParamT)> callback)
{}

main()
{    
    foo<int, int>([](int x){ return x; });  // no instance of function 
                                            //   template matches argument list
    bar<int, int>([](int x){ return x; });  // OK
}

Единственное различие между foo и bar заключается в том, что foo имеет вариативные аргументы. Как-то компилятор способен преобразовать лямбда в std :: function в bar.

Насколько я понимаю, вычет типа шаблона не учитывает преобразования типов. Так не должны ли оба терпеть неудачу?

Ответ 1

template<typename ReturnT, typename... ParamT>
void foo(std::function<ReturnT(ParamT...)> callback)
{}

теперь foo<int,int> является foo<ReturnT=int, ParamsT starts with {int}>.

Он не полностью определяет ParamT. Фактически, нет возможности полностью указать ParamT.

Как неполностью указанный шаблон, происходит дедукция и не выполняется. Он не пытается "что, если я просто предполагаю, что пакет больше не идет".

Вы можете исправить это с помощью:

template<typename ReturnT, typename... ParamT>
void foo(block_deduction<std::function<ReturnT(ParamT...)>> callback)
{}

где block_deduction выглядит так:

template<class T>
struct block_deduction_helper { using type=T; }:
template<class T>
using block_deduction = typename block_deduction_helper<T>::type;

теперь дедукция блокируется по первому аргументу foo.

И ваш код работает.

Конечно, если вы перейдете в std::function он больше не будет автоматически выводить аргументы.

Обратите внимание, что вывод типа типа стирания типа aa, такого как std::function, обычно является запахом кода.

Замените оба:

template<class F>
void bar(F callback)
{}

если вы должны получить аргументы, используйте помощники свойств функций (их много на SO). Если вам просто нужно вернуть значение, есть признаки std которые уже работают.


В вы можете сделать это:

tempate<class R, class...Args>
void bar( std::function<R(Args...)> f ) {}
template<class F>
void bar( F f ) {
  std::function std_f = std::move(f);
  bar(std_f);
}

с помощью функции .

Ответ 2

У вас нет вычетов для параметров типа bar, они полностью указаны.

У вас все еще есть хвост пакета для вывода в foo, и это терпит неудачу, потому что лямбда не является std::function.