当我定义此功能时,
template<class A>
set<A> test(const set<A>& input) {
return input;
}
我可以使用代码中其他位置的test(mySet)
来调用它,而无需显式定义模板类型。但是,当我使用以下功能时:
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; });
调用此函数时
我收到以下错误:
错误:没有匹配函数来调用'filter(std :: set&amp;,main()::)'
但是,所有这些版本都可以工作:
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函数直接放在表达式中而不直接创建std::function
时,为什么c ++ 11无法猜测模板类型?
修改
根据评论中的Luc Danton的建议,这里是我之前的函数的替代方法,不需要明确传递模板。
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; });
调用而无需模板。
编译器甚至可以在某种程度上猜测返回类型,使用新的decltype关键字并使用新函数返回类型语法。下面是一个使用一个过滤函数将一个集转换为映射的示例,以及一个根据值生成键的函数:
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); });
答案 0 :(得分:60)
问题在于lambdas的性质。它们是根据标准具有固定属性集的函数对象,但它们不是函数。该标准确定lambda可以使用确切的参数类型转换为std::function<>
,如果它们没有状态,则可以转换为函数指针。
但这并不意味着lambda是std::function
也不是函数指针。它们是实现operator()
的唯一类型。
类型推导只会推导出确切的类型,没有转换(const / volatile资格除外)。因为lambda不是std::function
,所以编译器无法推断出调用中的类型:filter(mySet,[](int i) { return i%2==0; });
是任何std::function<>
实例化。
在其他示例中,在第一个示例中,您将lambda转换为函数类型,然后传递它。编译器可以推导出那里的类型,如第三个例子中std::function
是同一类型的右值(临时)。
如果你向模板提供实例化类型int
,第二个工作示例,演绎没有发挥作用,编译器将使用该类型,然后将lambda转换为适当的类型。
答案 1 :(得分:14)
忘记你的情况。因为这太复杂了,无法进行分析。
举个简单的例子:
template<typename T>
struct X
{
X(T data) {}
};
template<typename T>
void f(X<T> x) {}
现在将f
称为:
f(10);
在这里,您可能会认为T
将推断为int
和,因此,上述函数调用应该有效。嗯,事实并非如此。为了简单起见,假设有另一个构造函数,它将int
作为:
template<typename T>
struct X
{
X(T data) {}
X(int data) {} //another constructor
};
当我写T
时,应该推断出f(10)
是什么?好吧,T
可以任何类型。
请注意,可能还有许多其他此类情况。以此专业化为例:
template<typename T>
struct X<T*> //specialized for pointers
{
X(int data) {};
};
现在应该为T
调用f(10)
推导出什么?现在看起来更难了。
因此,它是不可导入的上下文,它解释了为什么您的代码不适用于std::function
这是一个相同的情况 - 只是看起来很复杂。请注意, lambdas 不是std::function
类型 - 它们基本上是编译器生成的类的实例(即它们与不同类型的函数比std::function
)。
答案 2 :(得分:0)
如果我们有:
template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
return lambda(2);
}
int r = myfunc([](int i) { return i + 1; });
它不会编译。 但是,如果您之前声明过:
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1));
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
您可以毫无问题地使用lambda参数调用函数。
这里有2条新代码。
首先,我们有一个函数声明,该函数声明仅对返回有用 基于给定模板的旧式函数指针类型 参数:
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};
第二,我们有一个函数,该函数采用模板参数来构建预期的lambda类型,称为“ getFuncType”:
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
有了正确的模板参数,现在我们可以调用真正的“ myfunc”了。 完整的代码将是:
template <typename R, typename T>
int myfunc(std::function<R(T)> lambda)
{
return lambda(2);
}
template <typename Func, typename Arg1>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr) -> decltype((*func)(*arg1)) {};
template <typename Func>
int myfunc(Func lambda)
{
return myfunc<int, decltype(getFuncType<Func, int>())>(lambda);
}
int r = myfunc([](int i) { return i + 1; });
您可以声明“ getFuncType”的任何重载以匹配您的lambda参数。例如:
template <typename Func, typename Arg1, typename Arg2>
static auto getFuncType(Func* func = nullptr, Arg1* arg1 = nullptr, Arg2* arg2 = nullptr) -> decltype((*func)(*arg1, *arg2)) {};