为什么std::function不能接受推断类型作为其模板参数?

4
#include <functional>

using namespace std;

template<class CharType>
void f1(CharType* str, function<bool(CharType)> fn_filter)
{}

template<class CharType>
void f2(CharType* str, function<bool(char)> fn_filter)
{}

void f3(char* str, char c)
{
    auto fn_filter = [=](char e) -> bool 
    {
        return e == c; 
    };

    f1(str, fn_filter); // error C2784
    f2(str, fn_filter); // OK
}

int main()
{
    f3("ok", 'k');
}

// error C2784: 'void f1(CharType *,std::function<bool(CharType)>)' 
// : could not deduce template argument for 'std::function<bool(CharType)>' 
// from 'f2::<lambda_36be5ecc63077ff97cf3d16d1d5001cb>'

我的编译器是VC++ 2013。

为什么f1没有按预期工作?


2
我知道这个答案,但是我想不出一个好的表达方式。如果没有其他人尝试,我稍后会想出一些东西。 - Collin Dauphinee
2个回答

9

这个lambda没有类型std::function<bool(char)>,它只是一些可调用对象,其具体定义类型由实现定义。

它可以被转换为std::function<bool(char)>,但这并不能帮助编译器推导出模板情况下的类型。例如,对于CharType,这个lambda可以转换为std::function<bool(CharType)>的可能性有很多。

编译器尝试将lambda的类型与模板函数的参数进行匹配。例如,lambda的类型为lambda_t_1234,模板参数为std::function<bool(CharType)>。这些类型不相关,不清楚在这里CharType应该是什么。

这也不是lambda或std::function<>特有的问题。在所有这样的情况下都会发生同样的事情:

template<typename Char>
void f(const std::basic_string<Char> &str) {
}

如果您尝试使用char*参数调用此模板函数,则无法正常工作,因为与模板参数的连接不清楚。

1
你的例子稍有不同,因为OP作为第一个参数具有显式CharType*并传递了一个char*,所以我们可以合理地期望在OP的非常特殊的情况下将CharType推导为char - Matthieu M.
@MSalters:不,他不是。对于第二个参数,CharType也被推断出来了,它并不是一个无法推断的上下文。试着传递任何 std::function<bool(T)>,其中 T 不等于 char。你会得到一个推断不匹配的错误。但你可以通过将其设置为无法推断的上下文,强制进行转换。 - Xeo
@Xeo:没错。你确实有两个可推导的上下文,但是在一个上下文中推导失败了。虽然这不是SFINAE的情况,但这种失败是致命的。 - MSalters
跟我重复一遍: lambdas 不是 std::function,而 std::function 也不是 lambdas。 - Yakk - Adam Nevraumont

4
编译器的问题在于决定使用哪个参数进行类型推导。如果您通过从第二个参数中断可能的推导并“强制”使用第一个参数来帮助编译器,它将按预期工作:
template<typename T> struct identity { using type = T; };

template<class CharType>
void f1(CharType* str, typename identity<function<bool(CharType)>>::type fn_filter)
{}

Live example


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接