【问题标题】:Substitution failure with `std::function` and previously deduced template parameter - why?`std::function` 和之前推导出的模板参数替换失败 - 为什么?
【发布时间】:2020-01-27 20:41:37
【问题描述】:

考虑以下代码:

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&lt;void(S&lt;T&gt;)&gt;得到std::function&lt;void(S&lt;int&gt;)&gt;

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

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

额外问题:这是否可以在未来的标准中进行更改,同时保持向后兼容性,或者这种替换不起作用的根本原因是什么?

【问题讨论】:

标签: c++ c++11 templates language-lawyer template-argument-deduction


【解决方案1】:

虽然我理解 std::function 参数的类型通常无法推断,因为它是非推断上下文。

这不是一个非演绎的上下文。恰恰相反。因为尝试对std::function 的参数进行推导,但参数不是 std::function,所以推导失败。从函数参数中扣除模板参数必须对所有函数参数一致。如果它失败了,它就完全失败了。

[temp.deduct.type]

2 在某些情况下,扣除是使用一组 类型 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 从第一个函数参数成功推导出来,没有什么可推导的了。所以推演算是成功了。

Live

【讨论】:

    【解决方案2】:

    虽然我了解通常无法推断std::function 参数的类型,因为它是非推断上下文,但在这种情况下,T 可以首先通过传递的参数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,因此, 你得到一个错误。

    【讨论】:

      猜你喜欢
      • 2013-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多