【问题标题】:Strange error while expanding parameter pack containing lambda types扩展包含 lambda 类型的参数包时出现奇怪的错误
【发布时间】:2021-11-14 00:43:06
【问题描述】:

在以下示例中,我有一个类似于 foo 的函数:

template <typename... Parameters>
void foo(std::function<void (Parameters &)>... functions) {
    // does interesting things with these functions
}

现在我想用一些 lambdas 调用这个函数,例如像这样:

foo([](const std::string & string) {});

不幸的是,这不起作用,因为我收到以下错误:

error: no matching function for call to 'foo'
note: candidate template ignored: could not match 'function<void (type-parameter-0-0 &)>' against '(lambda at file.cpp:50:23)'

AFAIK,也就是说,因为 lambdas 不能像那样隐式转换为 std::functions

解决此问题的一种方法是手动将 lambda 封装在 std::function 中,如下所示:

foo(std::function<void (const std::string &)>([](const auto & string) {}));

但是对于多个 lambda,这将变得非常乏味。

为了解决这个问题,我尝试创建一个包装函数,该函数检测使用辅助类型传递的 lambda 的参数类型,然后将 lambda 包装为正确的 std::function 类型。这是仅用于单个参数(即非可变参数)的包装函数:

template <typename Function>
void fooWrapped(Function && function) {
    foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType &)>(function));
}

帮助器类型FunctionTypeTraits 是这样实现的:

template <typename Function>
class FunctionTypeTraits:
    public FunctionTypeTraits<decltype(&std::remove_reference<Function>::type::operator())> {};

template <typename Param>
class FunctionTypeTraits<void (&)(Param &)> {
    typedef Param ParameterType;
};

现在我可以用我的 lambda 调用包装函数,编译器非常满意:

fooWrapped([](const std::string & string) {});

原则上,我现在应该可以像这样使fooWrapper 可变参数:

template <typename... Functions>
void fooWrapped(Functions &&... functions) {
    foo((std::function<void (typename FunctionTypeTraits<Functions>::ParameterType &)>(functions))...);
}

但这不起作用。如果我用完全相同的代码调用这个新函数,我会收到以下错误:

error: 'std::remove_reference<void ((lambda at file.cpp:50:23)::*)(const std::string &) const>::type' (aka 'void ((lambda at file.cpp:50:23)::*)(const std::string &) const') is not a class, namespace, or enumeration

我不太明白这个错误。为什么相同的方法适用于单个模板类型,但不适用于扩展的参数包?这可能只是一个编译器错误吗?
还有另一种方法,我可以实现使用 lambdas 调用 foo 的目标,而无需手动将它们中的每一个包装在 std::function 中?

【问题讨论】:

    标签: c++ lambda typetraits parameter-pack


    【解决方案1】:

    lambda 的operator() 的地址类型是void (Lambda::*)(Param&amp;) const 而不是void (&amp;)(Param &amp;),您需要将FunctionTypeTraits 的基本情况定义为:

    template <typename Function>
    struct FunctionTypeTraits:
      public FunctionTypeTraits<decltype(&std::remove_reference<Function>::type::operator())> {};
    
    template <typename Lambda, typename Param>
    struct FunctionTypeTraits<void (Lambda::*)(Param) const> {
      typedef Param ParameterType;
    };
    

    另外一点是,在您的fooWrapped 中,指定std::function 的类型应该是void (typename FunctionTypeTraits&lt;Function&gt;::ParameterType) 而不仅仅是ParameterType,因为后者不是函数类型:

    template <typename... Function>
    void fooWrapped(Function&&... function) {
      foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType)>(function)...);
    }
    

    Demo.

    【讨论】:

    • 哦,好的。您的第二点实际上只是 Stackoverflow 上的一个错误,而不是我实际上是如何尝试的。我将编辑我的问题以纠正此问题。但是第一个现在似乎可以工作了。我很好奇为什么它可以与非可变函数一起使用,但不能与参数包一起使用......
    • 没看过work,能否提供一个转载的例子?
    • 显然我的 IDE 显示了可变参数示例的错误,但没有显示非可变参数示例的错误...我只能在构建代码后看到它们。在我声称它们是真实的之前,我真的应该测试它们......对此感到抱歉。感谢您的帮助!
    猜你喜欢
    • 2012-07-26
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 1970-01-01
    • 2019-12-22
    相关资源
    最近更新 更多