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

2022-01-30 00:00:00 lambda templates c++ c++11

当我定义这个函数时,

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

我可以在代码的其他地方使用 test(mySet) 调用它,而无需显式定义模板类型.但是,当我使用以下功能时:

I can call it using test(mySet) elsewhere in the code without having to explicitly define the template type. However, when I use the following 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; }); 调用此函数时;我收到以下错误:

When I call this function using filter(mySet,[](int i) { return i%2==0; }); I get the following error:

错误:没有匹配函数调用‘filter(std::set&, main()::)’

error: no matching function for call to ‘filter(std::set&, main()::)’

但是,所有这些版本都可以工作:

However, all of these versions do work:

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

为什么不直接创建std::function,直接把lambda函数放在表达式里面,c++11就猜不出模板类型?

Why is c++11 unable to guess the template type when I put the lambda function directly inside the expression without directly creating a std::function?

根据评论中 Luc Danton 的建议,这是我之前使用的不需要显式传递模板的函数的替代方法.

Per advice of Luc Danton in the comments, here is an alternative to the function I had earlier that does not need the templates to be passed explicitly.

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; }); 不需要模板.

This can be called by set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; }); without needing the template.

编译器甚至可以在某种程度上猜测返回类型,使用新的 decltype 关键字和使用新的函数返回类型语法.这是一个将集合转换为映射的示例,使用一个过滤函数和一个基于值生成键的函数:

The compiler can even guess the return types to some extent, using the new decltype keyword and using the new function return type syntax. Here is an example that converts a set to a map, using one filtering function and one function that generates the keys based on the values:

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

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

It can also be called without using the template directly, as

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

推荐答案

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

The issue is on the nature of lambdas. They are function objects with a fixed set of properties according to the standard, but they are not a function. The standard determines that lambdas can be converted into std::function<> with the exact types of arguments and, if they have no state, function pointers.

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

But that does not mean that a lambda is a std::function nor a function pointer. They are unique types implementing operator().

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

Type deduction, on the other hand, will only deduce exact types, with no conversions (other than const/volatile qualifications). Because the lambda is not a std::function the compiler cannot deduce the type in the call: filter(mySet,[](int i) { return i%2==0; }); to be any std::function<> instantiation.

与其他示例一样,在第一个示例中,您将 lambda 转换为函数类型,然后将其传递.编译器可以在那里推断出类型,如第三个示例,其中 std::function 是相同类型的右值(临时).

As of the other examples, in the first one you convert the lambda to the function type, and then pass that. The compiler can deduce the type there, as in the third example where the std::function is an rvalue (temporary) of the same type.

如果您向模板提供实例化类型 int,第二个工作示例,推导不起作用,编译器将使用该类型,然后将 lambda 转换为适当的类型.

If you provide the instantiating type int to the template, second working example, deduction does not come into play the compiler will use the type and then convert the lambda to the appropriate type.

相关文章