【问题标题】:Deducing template arguments during partial ordering when parameters are function parameter pack当参数是函数参数包时,在部分排序期间推导模板参数
【发布时间】:2015-11-29 04:35:01
【问题描述】:

N4527 14.8.2.4 [temp.deduct.partial]

3 用于确定排序的类型取决于完成部分排序的上下文:

(3.1) — 在函数调用的上下文中,使用的类型是函数调用具有参数的函数参数类型。

(3.2) — 在调用转换函数的上下文中,使用转换函数模板的返回类型。

(3.3) — 在其他上下文 (14.5.6.2) 中使用函数模板的函数类型。

4 上面从参数模板中指定的每个类型以及从参数中指定的相应类型 模板用作PA的类型。

8 如果A 是从函数参数包转换而来的,而P 不是参数包,则类型推导失败。 否则,使用结果类型 PA,然后按照 14.8.2.5 中的描述进行扣除。如果P 是一个函数参数包,参数模板的每个剩余参数类型的类型A是 与函数参数包的 declarator-id 的类型P 相比。每次比较都推导出 由函数扩展的模板参数包中后续位置的模板参数 参数包。如果给定类型的推导成功,则考虑参数模板中的类型 至少与参数模板中的类型一样专业。 [ 例子:

template<class... Args>           void f(Args... args);        // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2>      void f(T1 a1, T2 a2);        // #3

f();        // calls #1
f(1, 2, 3); // calls #2
f(1, 2);    // calls #3; non-variadic template #3 is more
            // specialized than the variadic templates #1 and #2

为什么f(1, 2, 3); 调用#2?

我需要更多详细信息,包括:

1 是哪个上下文?

2 转换后的来源是什么?
例如#1 的转换 from 是 void (U)void (U...) 还是其他形式?(U 表示唯一类型)

14.5.6.2 [temp.func.order]/p3

为每个类型、非类型或模板模板参数(包括 模板参数包(14.5.3))分别合成一个唯一的类型、值或类模板 并将其替换为模板函数类型中该参数的每次出现。

3 推演中PA有哪些类型?例如

template <class T> void f(T);
int a = 1;
f(a);//P = T, A = int

【问题讨论】:

    标签: c++ templates c++11 variadic-templates overload-resolution


    【解决方案1】:

    为什么f(1, 2, 3); 调用#2?

    你的问题有很多问题(每个问题一个问题!),所以我会坚持那个问题。首先,我们进行模板推导。 #3 失败,但 #1 和 #2 成功:

    template<class... Args>
    void f(Args... args);        // #1, with Args = {int, int, int}
    template<class T1, class... Args> 
    void f(T1 a1, Args... args); // #2, with T1 = int, Args = {int, int}
    

    这两个函数都采用三个ints 的值,因此重载决议中的所有正常决胜局都无法解决歧义。所以我们到了最后一个:

    鉴于这些定义,如果对于所有参数 i,ICSi(F1) 为不比 ICSi(F2) 更差的转换顺序,然后
    — [...]
    F1F2 是函数模板特化,F1 的函数模板更特化 根据 14.5.6.2 中描述的部分排序规则,而不是 F2 的模板。

    规则是:

    第一步:合成类型[temp.func.order]:

    为每个类型、非类型或模板模板参数(包括 模板参数包(14.5.3))分别合成一个唯一的类型、值或类模板 并将其替换为模板函数类型中该参数的每次出现。

    所以我们有:

    void f(Pack1... args);         // #1
    void f(U2 a1, Pack2... args);  // #2
    

    第 2 步:按照 [temp.deduct.partial] 中的说明进行扣除。我们所处的上下文是一个函数调用,所以我们使用函数调用有参数的类型。

    首先,我们尝试从#1 推导出#2。也就是说,我们尝试将 (T1, Args...) 与 (Pack1...) 进行匹配。然后第一部分是P = T1, A = Pack1...。我们有:

    如果 A 是从函数参数包转换而来的,而 P 不是参数包,则类型推导失败。

    所以从#1 推导出#2 失败,所以参数Args... 至少不像T1, Args... 那样专门化。

    接下来,我们尝试从#2 推导出#1。也就是说,我们尝试将(Args...)(U2, Pack2...) 进行匹配。成功了,所以T1, Args... 至少和Args... 一样专业。

    由于#2 至少和#1 一样特化,而#1 至少不像#2 那样特化,我们可以说#2 更特化:

    函数模板F 至少与函数模板G 一样专业化 确定排序,F 中的类型至少与G 中的类型一样专业。 F 更专业 如果F 至少与G 一样专业,并且G 至少不如F 专业,则比G

    更专业的模板是首选,所以我们称之为#2。

    【讨论】:

      猜你喜欢
      • 2016-01-30
      • 2016-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-02
      • 2016-12-30
      • 2021-08-31
      相关资源
      最近更新 更多