当涉及std :: function或lambda函数时,C ++ 11不会推导出types

当我定义这个函数时,

template<class A> set<A> test(const set<A>& input) { return input; } 

我可以在代码中的其他地方使用test(mySet)来调用它,而不必显式定义模板types。 但是,当我使用以下function:

 template<class A> set<A> filter(const set<A>& input,function<bool(A)> compare) { set<A> ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

当我使用filter(mySet,[](int i) { return i%2==0; }); 我得到以下错误:

 error: no matching function for call to 'filter(std::set<int>&, main()::<lambda(int)>)' 

但是,所有这些版本有效:

 std::function<bool(int)> func = [](int i) { return i%2 ==0; }; set<int> myNewSet = filter(mySet,func); set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; }); set<int> myNewSet = filter(mySet,function<bool(int)>([](int i){return i%2==0;})); 

为什么当我把lambda函数直接放在expression式中而不直接创buildstd::function时,c ++ 11无法猜测模板types?

编辑:

根据Luc Danton在评论中的build议,这是我之前所做的不需要显式传递模板的函数的替代方法。

 template<class A,class CompareFunction> set<A> filter(const set<A>& input,CompareFunction compare) { set<A> ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

这可以通过set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; });来调用set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; }); 而不需要模板。

编译器甚至可以在一定程度上猜测返回types,使用新的decltype关键字并使用新的函数返回types语法。 下面是一个将集合转换为地图的例子,它使用一个过滤函数和一个基于这些值生成关键字的函数:

 template<class Value,class CompareType,class IndexType> auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> { map<decltype(index(*(input.begin()))),Value> ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret[index(*it)] = *it; } } return ret; } 

也可以不使用模板直接调用

 map<string,int> s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); }); 

问题在于lambda的性质。 它们是按照标准具有一组固定属性的函数对象,但它们不是函数。 这个标准决定了lambdas可以被转换成std::function<> ,其参数的确切types以及如果它们没有状态的函数指针。

但是这并不意味着一个lambda是一个std::function也不是一个函数指针。 它们是实现operator()唯一types。

另一方面,types扣除只会推导出确切的types,而不会进行转换(除const / volatile限定外)。 因为lambda不是std::function所以编译器不能在调用中推导出types: filter(mySet,[](int i) { return i%2==0; }); 是任何std::function<>实例。

至于其他的例子,在第一个例子中,将lambda转换为函数types,然后传递它。 编译器可以推断出那里的types,就像第三个例子中的std::function是一个相同types的右值(临时)。

如果你将实例化typesint提供给模板,那么第二个工作示例中,演绎不起作用,编译器将使用该types,然后将lambda转换为适当的types。

忘记你的情况。 因为分析过于复杂。

以这个简单的例子:

  template<typename T> struct X { X(T data) {} }; template<typename T> void f(X<T> x) {} 

现在打电话给f

  f(10); 

在这里,你可能会想, T会被推断为int 因此,上面的函数调用应该工作。 那么,事实并非如此。 为了使事情简单,想象一下另外一个构造函数,

  template<typename T> struct X { X(T data) {} X(int data) {} //another constructor }; 

那么当我写f(10)时,应该推导出什么T ? 那么, T可以任何types。

请注意,可能有许多其他的这种情况。 以这个专业化为例:

  template<typename T> struct X<T*> //specialized for pointers { X(int data) {}; }; 

现在对于f(10)应该推导出什么T ? 现在看来更难了。

因此,它是不可拒绝的上下文,这就解释了为什么你的代码不适用于std::function ,这是一个相同的情况 – 只是看起来很复杂。 请注意, lambdas不是std::functiontypes – 它们基本上是编译器生成的类的实例(即它们是不同于std::function函子)。