【问题标题】:How to understand the rules of partial ordering about T& and T const&如何理解 T& 和 T const& 的偏序规则
【发布时间】:2019-12-12 02:26:17
【问题描述】:
template <typename T>
void show(T&);       // #1
template <typename T>
void show(T const&); // #2

int main()
{
    int a = 0;
    show(a);        // #1 to be called
}

我对这些偏序规则感到困惑。以下是一些引用:[temp.deduct.partial]/5

在完成部分排序之前,某些转换是 对用于偏序的类型执行:

  • 如果P 是引用类型,则P 将替换为引用的类型。

  • 如果A 是引用类型,则A 将替换为所引用的类型。

[temp.deduct.partial]/6

如果 PA 都是引用类型(在被替换为 上面提到的类型),确定这两种类型中的哪一种(如果有的话) 比另一个更符合 cv 要求;否则类型是 对于部分排序目的,被认为是同样具有 cv 资格的。 下面将使用此确定的结果。

[temp.deduct.partial]/7

删除所有顶级 cv 限定符:

  • 如果 P 是 cv 限定类型,则 P 将替换为 P 的 cv 不限定版本。

  • 如果 A 是 cv 限定类型,A 将替换为 A 的 cv 不限定版本。

首先,void show(T&amp;)void show(T const&amp;)都可以通过传递int左值来调用,所以我们需要使用偏序规则来决定哪个函数更匹配。然后,根据上面的引用,我们做一些转换。第 1 步:

T&       => T          #3
T const& => T const    #4

第 2 步:

T       => T    #5
T const => T    #6

#5 =&gt; #6,#6 =&gt; #5,双向推演成功。 然后以下规则起作用:[temp.deduct.partial]/9

如果,对于给定的类型,推论在两个方向上都成功(即, 上述转换后类型相同)和PA 是引用类型(在被引用的类型替换之前 到上面):

  • 如果参数模板中的类型是左值引用,而参数模板中的类型不是,则参数类型为 不被认为至少与参数类型一样专业;

  • 否则,如果参数模板中的类型比参数模板中的类型更具 cv 限定(如所述 上),参数类型至少不被认为是 专门作为参数类型。

所以#4#3 更专业。对于给定值a,应该调用#2 函数,但实际上调用了#1 函数。为什么?我的理解有问题吗?

【问题讨论】:

  • 我也很好奇这个答案,是不是因为 const&amp; 不是你声明和传递的?所以它只将值作为参考。如果您使用const int = 0,我认为第二个显示功能,const&amp; 将被调用。
  • @Raymond 如果你注释掉这些方法中的任何一个,剩下的函数也是匹配的,所以它们应该根据部分排序规则来决定哪个更匹配,但是部分排序规则选择#2,编译器选择#1,可能是对这些部分排序规则的补充让编译器使用#1,需要知道的人告诉我们
  • 嗨,jack X。我已经编辑了您的问题,以包含指向标准相关部分的链接。这有助于其他人收集上下文。请在以后的问题中做同样的事情。谢谢!

标签: c++ language-lawyer template-argument-deduction function-templates partial-ordering


【解决方案1】:

在这种情况下,不使用“更专业”的规则,这是一个决胜局,以防对隐式转换序列进行排序并不能确定函数的顺序:[over.match.best]/1

如下定义ICSi(F):

[...]

鉴于这些定义,一个可行的函数 F1 被定义为 如果所有参数都比另一个可行的函数 F2 更好的函数 i,ICSi(F1)不是比ICS更差的转换序列i(F2),然后是

  • (1.3) 对于某些参数 j,ICSj(F1) 是比 ICSj 更好的转换序列(F2),或者,如果不是,

  • [...]

  • (1.7) F1和F2是函数模板特化,F1的函数模板比​​F1的模板更特化 F2 根据部分排序规则描述 [temp.func.order], 或者,如果不是,

  • [...]

在这种情况下,仅对隐式转换序列进行排序就足以确定排序,因此“或,如果不是”之后的所有内容都将被忽略。 #1 的推导结果为 (int&amp;),#2 的推导结果为 (int const&amp;),因此在这两种情况下,引用绑定(aint&amp;aaint const&amp;)都会导致身份转换,而不管简历资格:[over.ics.ref]/1

当引用类型的参数直接绑定到参数表达式时,隐式转换序列是身份 转换,除非参数表达式的类型是 参数类型的派生类,在这种情况下隐式 转换序列是派生到基础的转换 ([over.best.ics])。 [...]

但是,由于[over.ics.rank]/3,ICS1(#1) 是比 ICS1(#2) 更好的转换序列:

两个相同形式的隐式转换序列是 不可区分的转换序列,除非以下之一 规则适用:

  • [...]

  • (3.2) 标准转换序列 S1 是比标准转换序列 S2 更好的转换序列 if

    • [...]

    • (3.2.6) S1 和 S2 是引用绑定 ([dcl.init.ref]),并且引用所引用的类型是相同的类型,除了 对于顶级 cv 限定符,和引用的类型 由 S2 初始化的引用比它所指向的类型更具有 cv 限定 S1 初始化的引用是指

  • [...]

因此,ICS1(F1) 是比 ICS1(F2) 更好的转换序列,因此根据[over.match.best]/(1.3)(上图),F1 优于 F2。未使用[over.match.best]/(1.7) 规则。


在相反的情况下使用“更专业”的规则:

int const a = 0;
show(a);         // #2 should be called for a const lvalue

这一次,推导结果是int const&amp;int const&amp;,所以[over.match.best]/(1.7) 开始了。结果,正如您所观察到的,#2 是一个比#1 更好的函数。


(强调我的,所有引号)

【讨论】:

  • 我大概明白了,但是对于“和引用所指的类型除了顶级的cv-qualifiers之外的类型都是相同的”有一些疑问,例如这个怎么理解? T const value"和"U value",如果T与U相同则条件为真或"T value"和"U value",只有T与U相同且两者都不包含" const",条件为真?哪个是正确的理解?
  • @jackX “除了顶级 cv 限定符之外的相同类型”表示“相同类型,在 const 和 volatile 被删除之后”。例如,const intint 条件为真,intint 也为真,但 intdoubleintint* 为假。
  • 感谢“并且由 S2 初始化的引用所引用的类型比由 S1 初始化的引用所引用的类型更具 cv 限定”,这意味着,如果参数是“T 值” ,F1的参数是“U&”,F2的参数是“U const&”,如果T不包含cv,那么F1更匹配,如果T包含cv,那么F2比F1更匹配?
  • @jackX 没错。不过,这两个结论是由不同的规则得出的,如答案所示:)
  • L.F.你太谦虚了:)
猜你喜欢
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
  • 2021-02-18
  • 1970-01-01
  • 2013-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多