C++ 使用“std::function”和以前推导的模板参数替换失败-为什么?

C++ 使用“std::function”和以前推导的模板参数替换失败-为什么?,c++,c++11,templates,language-lawyer,template-argument-deduction,C++,C++11,Templates,Language Lawyer,Template Argument Deduction,考虑以下代码: template <typename> struct S { }; void g(S<int> t); template <typename T> void f(T, std::function<void(S<T>)>); 我得到以下错误: 错误:调用“f”时没有匹配的函数 f(0,g); ^ 注意:已忽略候选模板:无法匹配 “功能” 针对“无效(*)(S)”的 void f(T,std::function);

考虑以下代码:

template <typename> 
struct S { };

void g(S<int> t);

template <typename T>
void f(T, std::function<void(S<T>)>);
我得到以下错误:

错误:调用“f”时没有匹配的函数
f(0,g);
^
注意:已忽略候选模板:无法匹配
“功能”
针对“无效(*)(S)”的
void f(T,std::function);
^

虽然我知道通常不能推断
std::function
参数的类型,因为它是非推断上下文

在这种情况下,可以首先通过传递的参数
0
推导
T
,然后将其替换为
std::function
,得到
std::function

我希望在推导
T=int
之后,编译器将替换签名中的所有
T
,然后尝试使用参数
g
构造
std::function
参数

为什么不是这样?我认为替换/扣除发生的顺序与此有关,但我想看看相关的标准措辞

附加问题:这是一个在未来的标准中可能会改变的东西,同时保留向后兼容性,还是有一个根本原因导致这种替代不起作用

虽然我知道std::function参数的类型通常不能推断,因为它是一个未推断的上下文

这不是一个非推断的上下文。恰恰相反。由于尝试对
std::function
的参数进行推断,但该参数不是
std::function
,因此推断失败。从函数参数中推断模板参数必须与所有函数参数一致。如果其中一个失败了,那么它就完全失败了

[临时扣减类型]

在某些情况下,使用一组单独的 类型P和A,在其他情况下,将有一组相应的 类型P和A。每个P/A对的类型扣减是独立进行的, 然后合并推导出的模板参数值。如果类型 不能对任何P/A对进行扣减,或者如果对任何P/A对进行扣减 推断会导致多个可能的推断值集,或者 不同的对产生不同的推导值,或者如果有模板 参数既不是推导的,也不是显式指定的,模板 论证推理失败

将第二个函数参数的类型转换为非推断上下文实际上是克服错误的方法

#include <functional>

template<typename T>
struct type_identity {
    using type = T;
};

template <typename> 
struct S { };

void g(S<int> ) {}

template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}

int main() {
    f(0, g);
}
#包括
模板
结构类型标识{
使用类型=T;
};
模板
结构S{};
无效g(S){}
模板
void f(T,typename type_identity::type){}
int main(){
f(0,g);
}
T
是从第一个函数参数成功推导出来的,没有什么可以推导的了。因此,这个决定被认为是成功的

虽然我知道通常不能推断
std::function
参数的类型,因为它是非推断上下文,但在这种情况下,可以首先通过传递的参数
0
推断
t

事实并非如此
T
在这种情况下是可以推断的。如果您将代码更改为

template <typename T>
void f(std::function<void(S<T>)>);

int main()
{
    f(std::function<void(S<int>)>(g));
}
模板
void f(std::function);
int main()
{
f(std::function(g));
}
代码将被编译并且
T
被正确推导


您的问题是,您正在将一个对象传递给它无法从中提取
t
的函数。当编译器试图推断
T
时,它不会对函数参数进行任何转换。这意味着您有一个
int
和一个函数作为传递给该函数的类型。它从
0
中获取
int
,然后尝试从您在第二个参数中传递的
std::function
中获取类型,但是由于您没有传递
std::function
,因此无法提取
t
,因此,您会得到一个错误。

这里没有
std::function
或函数指针,情况也是如此,简化一点:您仍然可以使用
模板void f(T,std::identity\u type\T)
@Jarod42是否
std::identity\u type\T
进行标准化?@MaxLanghof
#include <functional>

template<typename T>
struct type_identity {
    using type = T;
};

template <typename> 
struct S { };

void g(S<int> ) {}

template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}

int main() {
    f(0, g);
}
template <typename T>
void f(std::function<void(S<T>)>);

int main()
{
    f(std::function<void(S<int>)>(g));
}