`std::function` 和先前推导的模板参数的替换失败 - 为什么?

Substitution failure with `std::function` and previously deduced template parameter - why?

考虑以下代码:

template <typename> 
struct S { };

void g(S<int> t);

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

尝试调用时

f(0, g);

我收到以下错误:

error: no matching function for call to 'f'
    f(0, g);
    ^

note: candidate template ignored: could not match 
      'function<void (S<type-parameter-0-0>)>' 
      against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
     ^

live example on godbolt.org

虽然我知道通常无法推断出 std::function 参数的类型,因为它是一个非推断的上下文

在这种情况下T可以先由传递的参数0推导出来,然后代入std::function<void(S<T>)>得到std::function<void(S<int>)>.

我希望在推导 T=int 之后,编译器会在签名中的任何地方替换 T,然后尝试使用参数 g 构造 std::function 参数.

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

奖金问题:这是否可能在未来的标准中更改,同时保持向后兼容性,或者是否存在这种替代不起作用的根本原因?

While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context.

这不是非推导上下文。恰恰相反。因为试图推导std::function的参数,但是参数是而不是一个std::function,推导失败。从函数参数中推导模板参数必须对所有函数参数一致。如果一个失败,那就是彻底失败。

[temp.deduct.type]

2 In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template argument deduction fails.

将第二个函数参数的类型设置为非推导上下文实际上是克服错误的方法。

#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 从第一个函数参数推导成功,没有什么可推导的了。所以推算成功。

Live

While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context, in this case T can first be deduced by the passed argument 0.

这不是真的。 T 在这种情况下是可以推导出来的。如果将代码更改为

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

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

代码将编译并且 T 被正确推导。

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