选择用于分配初始化的转换函数的优先级

时间:2018-03-16 17:28:05

标签: c++ type-conversion qualifiers

请考虑以下代码段:

class A;

class B { 
      public: 
         B(){} 

         B(A&) // conversion constructor that takes cv-unqualified A
         { 
              cout << "called B's conversion constructor" << endl; 
         } 
};

class A { 
      public: 
         operator B() const // conversion operator that takes cv-qualified A
         { 
              cout << "called A's conversion operator" << endl; 
              return B(); 
         } 
};

int main()
{
    B b = A(); // who gets called here?
    return 0;
}

根据this question,具有最少cv限定形式的转换序列获胜(规范中为13.3.3.2/3):

  

标准转换序列S1 是一个更好的转换序列   标准转换序列S2,如果S1和S2是参考   绑定(8.5.3),以及引用引用的类型   除了顶级cv限定符之外的相同类型,以及 的类型   由S2引用初始化的引用比更符合cv   由S1初始化的引用引用的类型

然而在上面的代码片段中,转换运算符总是被选中,无论A在两个函数中是否具有cv限定。唯一的例外是,当构造函数和运算符都是cv限定的时候,编译器会抱怨选择转换序列时不明确,而cv-unqualified case都没有(为什么?)。

所以问题是:

  1. 为什么在这种情况下总是会选择转换运算符?
  2. 为什么两个cv-qualified都会引起歧义,而cv-nonqualified都没有?

1 个答案:

答案 0 :(得分:1)

出于重载解析的目的,A::operator B()有一个隐式对象参数,其类型为cv A&。该参数是特殊的,即使它是根据[over.match.funcs]/5的非const类型的左值引用,它也可以绑定到右值:

  

在重载解析期间,隐含的对象参数与​​其他参数无法区分。但是,隐式对象参数保留其标识,因为不能应用用户定义的转换来实现与它的类型匹配。对于未使用ref-qualifier声明的非静态成员函数,将应用其他规则:

     
      
  • 即使隐式对象参数不是const限定的,也可以将rvalue绑定到参数,只要在所有其他方面,参数可以转换为隐式对象参数的类型。 [注意:这样的参数是rvalue的事实不会影响隐式转换序列的排名。 - 尾注]
  •   

[over.ics.ref]/3

  

除了隐式对象参数,参见[over.match.funcs],如果需要将除非对非易失性const类型的引用之外的左值引用绑定到右值,则无法形成标准转换序列或将右值引用绑定到函数左值以外的左值。 [注意:这意味着,例如,如果候选函数具有非const左值引用参数(隐式对象参数除外),则候选函数不能成为可行函数,并且相应的参数将需要创建临时函数来初始化左值参考(参见[dcl.init.ref])。 - 结束说明]

因此,如果转换构造函数采用非const参数,则转换运算符始终可行时不可行,这使得重载决策始终选择转换运算符。

如果转换构造函数采用const参数且转换运算符是非const,则两个隐式转换都是[over.ics.ref]/1的标识转换:

  

当引用类型的参数直接绑定到参数表达式时,隐式转换序列是标识转换...

然后根据[over.ics.rank]/3

  

标准转换序列S1是比标准转换序列S2更好的转换序列,如果

     
      
  • ...

  •   
  • S1和S2是引用绑定,引用引用的类型除顶级cv限定符外属于同一类型,S2引用初始化引用的类型更符合cv比S1引用的引用所引用的类型。

  •   

因此选择了非const版本(转换运算符)。

最后,如果两者都是const,它们之间没有区别,也没有特殊规则适用,因此重载决议因模糊而失败。