【问题标题】:No matching function call for lambda function parameter to template function没有将 lambda 函数参数与模板函数匹配的函数调用
【发布时间】:2021-06-28 19:25:56
【问题描述】:

尝试将 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)};
}

【问题讨论】:

  • @AndyG 不管有没有std::function,我认为OP是在问如何从lambda中推断出参数类型。
  • @RichardCritten 因为auto test_object = test_type&lt;int, double&gt;{[](int a, double b) { std::cout &lt;&lt; a &lt;&lt; b &lt;&lt; "\n"; }} 按预期工作。至少当我指定构造函数取std::function&lt;void(TArgs...)&gt;时是这样,但工厂函数由于某种原因不能这么说。

标签: c++ lambda variadic-templates c++20


【解决方案1】:

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

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

之后只需编写样板文件来处理传递给make_test的以下可调用对象

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

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

从我们将专门针对每个场景的基类开始:

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

【讨论】:

  • 也可以加推导:template&lt;class T&gt; test_type(T&amp;&amp;) -&gt; infer_test_type_t&lt;T&gt;;.
  • @Jarod42: 不幸的是,推导指南中-&gt; 右侧的简单模板 id 中的 模板名称 必须与 模板名称 匹配每个 [temp.deduct.guide] 的类,由于需要推断可变参数,我在这里看不到一个好的解决方法。
  • 谢谢!对于看似如此简单的事情来说,这真是一团糟……你提到了概念和约束,这会让这更好吗?
  • @wich,不幸的是不多。我们必须先建立一些东西。可重用的东西,但如果你没有做很多元编程,那么它可能不值得
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-23
  • 2018-07-02
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多