Проблема
Проблема, с которой вы столкнулись, может быть сведена к следующему:
#include <functional>
template <typename ...Args>
void foo(std::function<bool(Args...)>) { }
int main()
{
foo([](int, int) { return true; });
}
, которая не скомпилируется. Причина этого заключается в том, что вывод типа для аргументов шаблона для std::function
завершается неудачей.
Вы ожидаете, что std::function
некоторого вида будет передано в качестве аргумента. Поскольку объект std::function
(независимо от точной реализации) не передается, компилятор пытается создать std::function
и определить типы параметров шаблона, вызвав конструктор std::function
. Здесь начинается проблема. Соответствующий конструктор в этом случае будет:
template <typename F>
function(F f);
Вы заметите, что сам конструктор также является шаблоном. Компилятор может успешно вывести F
как лямбду, но, поскольку F
является параметром шаблона конструктора, параметр шаблона самого класса std::function
не может быть выведен.
Кроме того, для цитирования cppreference для этого конструктора:
[...] Этот конструктор не участвует в разрешении перегрузки, если только для параметра типа аргумента Args ... и типа R. [. ..]
Это означает, что существование этого конструктора основано на том, можно ли F
вызывать с аргументами шаблона класса Args...
, но поскольку они не определены явно и не могут быть выведены этот конструктор все равно не будет доступен.
Решение
Поскольку вы используете только std::function
внутри exportSelectedData
, просто сделайте его параметром шаблона полностью (исключая std::function
) part):
template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;
Вам также следует изменить Args_T&&
на Args_T const&
, поскольку вы не просто перенаправляете эти аргументы, но повторно используете их внутри al oop.
E dit
Относительно вашего дополнительного вопроса в редакции: подумайте о том, что вы делаете.
Сначала вы объявляете лямбду:
auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };
Теперь подумайте о подпись этой лямбды. Вы возвращаете логическое значение, поэтому тип возвращаемого значения bool
(пока все хорошо). Вы берете u32MaxCmdDuration
в предложении захвата и принимаете один аргумент singleElement
. Давайте удалим все квалификаторы extra и посмотрим на подпись:
bool(CombinedDictElement) // take a CombinedDictElement and return a bool
Далее мы рассмотрим вызов exportSelectedData
:
exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);
Вы передаете u32MutexTimeout
и lambda
, что совершенно нормально, лямбда захвачена compareFn
. Третий аргумент - u32MaxCmdDuration
, который фиксируется ...compareArgs
в вашем шаблоне. Теперь давайте посмотрим, где вы на самом деле вызываете лямбду внутри exportSelectedData
:
if (compareFn(singleElement, compareArgs...)) // ...
Какую подпись вы ожидаете compareFn
здесь? Если развернуть пакет ...compareArgs
(опять же, для простоты удаляем extra квалификаторы), подпись будет выглядеть так:
bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool
Снова лямбда-сигнатура:
bool(CombinedDictElement)
Вы замечаете проблему? Лямбда захватывает u32MaxCmdDuration
как захват состояния, тогда как exportSelectedData
ожидает его в качестве параметра (поскольку вы передали его в exportSelectedData
в качестве дополнительного аргумента). Очевидно, подписи отличаются друг от друга, поэтому мы должны изменить одну из них, чтобы она соответствовала другой. Это довольно просто в вашем случае, либо
измените лямбду на u32MaxCmdDuration
в качестве параметра:
auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)
или
удалить u32MaxCmdDuration
в качестве дополнительного аргумента для вашего exportSelectedData
вызова:
exportSelectedData(u32MutexTimeout, lambda);