扩展包含 lambda 类型的参数包时出现奇怪错误
Strange error while expanding parameter pack containing lambda types
我有一个函数,在下面的例子中看起来像 foo
:
template <typename... Parameters>
void foo(std::function<void (Parameters &)>... functions) {
// does interesting things with these functions
}
现在我想用一些 lambda 来调用这个函数,例如像这样:
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,也就是说,因为 lambda 不能像那样隐式转换为 std::functions
。
解决此问题的一种方法是手动将 lambdas 包装在 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
我不太明白这个错误。为什么同样的方法适用于单个模板类型,但不适用于扩展参数包?这可能只是一个编译器错误吗?
有没有另一种方法,我可以实现使用 lambda 调用 foo
的目标,而无需手动将它们中的每一个包装在 std::function
?
中
lambda operator()
的地址类型是 void (Lambda::*)(Param&) const
而不是 void (&)(Param &)
,您需要将 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<Function>::ParameterType)
而不是 ParameterType
因为后者不是函数类型:
template <typename... Function>
void fooWrapped(Function&&... function) {
foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType)>(function)...);
}
我有一个函数,在下面的例子中看起来像 foo
:
template <typename... Parameters>
void foo(std::function<void (Parameters &)>... functions) {
// does interesting things with these functions
}
现在我想用一些 lambda 来调用这个函数,例如像这样:
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,也就是说,因为 lambda 不能像那样隐式转换为 std::functions
。
解决此问题的一种方法是手动将 lambdas 包装在 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
我不太明白这个错误。为什么同样的方法适用于单个模板类型,但不适用于扩展参数包?这可能只是一个编译器错误吗?
有没有另一种方法,我可以实现使用 lambda 调用 foo
的目标,而无需手动将它们中的每一个包装在 std::function
?
lambda operator()
的地址类型是 void (Lambda::*)(Param&) const
而不是 void (&)(Param &)
,您需要将 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<Function>::ParameterType)
而不是 ParameterType
因为后者不是函数类型:
template <typename... Function>
void fooWrapped(Function&&... function) {
foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType)>(function)...);
}