当涉及std :: function或lambda函数时,C ++ 11不推断类型

时间:2012-04-03 17:32:51

标签: c++ templates lambda c++11

当我定义此功能时,

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); });

3 个答案:

答案 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)) {};