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

问题描述 投票:53回答:3

当我定义这个功能时,

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&,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); });
c++ templates lambda c++11
3个回答
60
投票

问题在于lambdas的性质。它们是根据标准具有固定属性集的函数对象,但它们不是函数。该标准确定lambdas可以使用确切的参数类型转换为std::function<>,如果它们没有状态,则可以转换为函数指针。

但这并不意味着lambda是std::function也不是函数指针。它们是实现operator()的独特类型。

另一方面,类型推导只会推导出确切的类型,没有转换(const / volatile资格除外)。因为lambda不是std::function,所以编译器不能推断出调用中的类型:filter(mySet,[](int i) { return i%2==0; });是任何std::function<>实例化。

从其他示例开始,在第一个示例中,您将lambda转换为函数类型,然后传递它。编译器可以推导出那里的类型,如第三个例子中std::function是相同类型的rvalue(临时)。

如果你向模板提供实例化类型int,第二个工作示例,演绎没有发挥作用,编译器将使用该类型,然后将lambda转换为适当的类型。


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这是一个相同的情况 - 只是在表面看起来很复杂。请注意,lambda不是std::function类型 - 它们基本上是编译器生成的类的实例(即它们是std::function不同类型的仿函数)。


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)) {};
© www.soinside.com 2019 - 2024. All rights reserved.