没有将 lambda 函数参数与模板函数匹配的函数调用

No matching function call for lambda function parameter to template function

尝试将 lambda 函数传递给以传递函数的函数参数为模板的模板工厂函数导致 gcc-10.2.0 报告 no matching function for call to ‘test(main()::<lambda(int, double)>)’.

当我在 lambda 函数前面添加一个 + 强制转换为函数指针时,它似乎确实有效,但我不明白为什么有必要这样做。为什么转换不会自动发生?有什么办法可以做到这一点吗?

我也试过 std::function<void(TArgs...)> test_func 作为 make_test 声明中的参数,但是这给了我同样的 no matching function for call 错误。

#include <iostream>

template <typename... TArgs>
struct test_type {
  test_type(void(TArgs...)) {};
};

template <typename... TArgs>
test_type<TArgs...> make_test(void test_func(TArgs...)) {
  return test_type<TArgs...>{test_func};
}

int main() {
  auto test_object = make_test([](int a, double b) { std::cout << a << b << "\n"; });

  return 0;
}

编辑

我想知道是否有某种方法可以使其与类型特征一起使用。像下面这样的东西。虽然我知道没有办法从模板参数中获取参数列表。


template <typename F>
test_type<get_arg_list<F>> make_test(std::function<F>&& f) {
  return test_type<get_arg_list<F>>{std::forward(f)};
}

为了支持传递给工厂的各种可调用对象(例如,有状态的 lambda 或函数指针),您的 test_type 构造函数应该接受某种类型擦除的函数类型,例如 std::function<void(int, double)>:

template<class... TArgs>
struct test_type {
  test_type(std::function<void(TArgs...)>) {};
};

之后,只需编写样板来处理以下传递给 make_test

的可调用对象
  • 常规函数指针
  • 一个 lambda(具有 operator()(...) const 的结构)
  • 一个可变的 lambda 或一个没有 const operator() 函数的用户定义的可调用函数

这是一种使用类型特征的方法:

从一个基础 class 开始,我们将专门针对每个场景:

template<class T, class = void>
struct infer_test_type;

(这是 voider 模式的常见设置。我们可以通过概念和约束来做到这一点,但我懒得查语法,也许以后再看)

常规函数指针特化

template<class Ret, class... Args>
struct infer_test_type<Ret(*)(Args...)>
{
    using type = test_type<Args...>;
};

现在我们可以为简单起见编写一个模板化别名:

template<class T>
using infer_test_type_t = typename infer_test_type<T>::type;

我们可以验证它是这样工作的:

void foo(int, double){}
// ... 
static_assert(std::is_same_v<infer_test_type_t<decltype(&foo)>, test_type<int, double>>);

我们可以像这样为 make_test 函数使用类型特征:

template<class T>
auto make_test(T&& callable) -> infer_test_type_t<T>
{
    return infer_test_type_t<T>{std::forward<T>(callable)};
}

现在只需涵盖我们的其他两个场景。

可调用对象

  • 这些将有 operator()(const 或没有)

我将从顶级特征开始检测 operator() 的存在并将 operator() 的类型输入另一个特征。

顶级特征:

// if T is a callable object
template<class T>
struct infer_test_type<T, std::void_t<decltype(&T::operator())>>
{
    using type = typename infer_test_type<decltype(&T::operator())>::type;
};

你看到它在内部回调到我还没有展示的另一个 infer_test_type 专业化;专门用于 operator() 的一种。我现在将展示这两个专业:

// if operator() is a const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...) const>
{
    using type = test_type<Args...>;
};

// if operator() is non-const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...)>
{
    using type = test_type<Args...>;
};

(如果我们想更聪明一点,我们可以将这两者结合起来,并在跟注之前在高级别去掉任何 const,但我认为这更清楚)

现在我们应该能够为 非泛型 可调用对象(没有泛型 lambda 或模板化 operator() 函数)推断出合适的 test_type

一个非常量测试 operator():

struct my_callable
{
    void operator()(int, double) // non-const
    {
    }
};

// ...
static_assert(std::is_same_v<infer_test_type_t<my_callable>, test_type<int, double>>);

并使用您的 lambda 进行测试:

auto lambda = [](int a, double b) { std::cout << a << b << "\n"; };
static_assert(std::is_same_v<infer_test_type_t<decltype(lambda)>, test_type<int, double>>);

综合起来

对于您的简单(非捕获、非通用 lambda)示例,它非常简单:

make_test([](int a, double b) { std::cout << a << b << "\n"; });

Demo