为什么C ++ 11不会将lambdas转换为std :: function对象?
我实现了一个通用事件发射器类,它允许代码注册回调,并用参数发出事件。 我使用Boost.Any类型的擦除来存储回调,以便他们可以有任意的参数签名。
这一切都有效,但由于某种原因,传入的lambda必须先转换为std::function
对象。 为什么编译器不推断lambda是函数类型? 是因为我使用可变模板的方式吗?
我使用Clang(版本字符串: Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
)。
码:
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <boost/any.hpp>
using std::cout;
using std::endl;
using std::function;
using std::map;
using std::string;
using std::vector;
class emitter {
public:
template <typename... Args>
void on(string const& event_type, function<void (Args...)> const& f) {
_listeners[event_type].push_back(f);
}
template <typename... Args>
void emit(string const& event_type, Args... args) {
auto listeners = _listeners.find(event_type);
for (auto l : listeners->second) {
auto lf = boost::any_cast<function<void (Args...)>>(l);
lf(args...);
}
}
private:
map<string, vector<boost::any>> _listeners;
};
int main(int argc, char** argv) {
emitter e;
int capture = 6;
// Not sure why Clang (at least) can't deduce the type of the lambda. I don't
// think the explicit function<...> business should be necessary.
e.on("my event",
function<void ()>( // <--- why is this necessary?
[&] () {
cout << "my event occurred " << capture << endl;
}));
e.on("my event 2",
function<void (int)>(
[&] (int x) {
cout << "my event 2 occurred: " << x << endl;
}));
e.on("my event 3",
function<void (double)>(
[&] (double x) {
cout << "my event 3 occurred: " << x << endl;
}));
e.on("my event 4",
function<void (int, double)>(
[&] (int x, double y) {
cout << "my event 4 occurred: " << x << " " << y << endl;
}));
e.emit("my event");
e.emit("my event 2", 1);
e.emit("my event 3", 3.14159);
e.emit("my event 4", 10, 3.14159);
return EXIT_SUCCESS;
}
lambda不是std::function
,而std::function
不是lambda。
lambda是创建匿名类的语法糖,它看起来像这样:
struct my_lambda {
private:
int captured_int;
double captured_double;
char& referenced_char;
public:
int operator()( float passed_float ) const {
// code
}
};
int captured_int = 7;
double captured_double = 3.14;
char referenced_char = 'a';
my_lambda closure {captured_int, captured_double, referenced_char};
closure( 2.7f );
由此:
int captured_int = 7;
double captured_double = 3.14;
char referenced_char = 'a';
auto closure = [=,&referenced_char](float passed_float)->int {
// code
};
closure(2.7);
与my_lambda
的类型名称实际上是一些无名类型。
std::function
是完全不同的东西。 它是一个用特定签名实现operator()
并将智能值语义指针存储到包含复制/移动/调用操作的抽象接口的对象。 它有一个template
d的构造函数,它可以采用任何支持copy / move / operator()
和兼容签名的类型,生成一个实现抽象内部接口的具体自定义类,并将其存储在上述内部值语义智能指针。
然后它将操作从其自身作为值类型转发到抽象内部指针,包括完美转发到调用方法。
碰巧,你可以将一个lambda存储在一个std::function
,就像你可以存储一个函数指针一样。
但是有很多不同的std::function
可以存储一个给定的lambda - 任何类型都可以转换为参数的函数,实际上它的工作原理也是一样的,就std::function
而言。
C ++中的template
类型扣除不能在“您可以转换为”的级别上工作 - 它是模式匹配,纯粹且简单。 由于lambda是与任何std::function
无关的类型,因此不能从中推导出std::function
类型。
如果C ++试图在一般情况下这样做,它将不得不颠倒Turing-complete过程来确定可以将哪些类型(如果有)传递给template
以便生成兼容转换的实例。
从理论上讲,我们可以在语言中添加“运算符演绎模板参数”,在该语言中,给定template
的实现者可以编写采用某种任意类型的代码,并且他们试图从这种类型中梳理出应该使用哪些template
参数为一个实例“。 但是C ++没有这个。
编译器不会推断任何内容,因为编译器实现了C ++语言,并且语言的模板参数推导规则不允许以您想要的方式进行推导。
以下是一个简单的例子,代表您的情况:
template <typename T> struct Foo
{
Foo(int) {}
};
template <typename T> void magic(Foo<T> const &);
int main()
{
magic(10); // what is T?
}
当boost::any
存储一个值时,它使用该对象的静态类型来确定正在存储什么类型的对象。 如果您指定存储的内容的静态类型,则只能将any
回正确类型的对象。
每个C ++ lambda都与用户不透明的实现定义类型相关联。 虽然lambda可以称为函数,但它们不直接评估为std::function
s。 在将lambda存储在any
时,强制转换是必要的,以确保存储的静态类型是您转回时的std::function
。
希望这可以帮助!
链接地址: http://www.djcxy.com/p/51577.html上一篇: Why doesn't C++11 implicitly convert lambdas to std::function objects?
下一篇: Using variadic template arguments to resolve a lambda signature