为什么第一个函数调用绑定到第一个函数?

时间:2014-11-03 22:44:56

标签: c++ function templates language-lawyer overload-resolution

为什么第一个函数调用(cm(car);)绑定到第一个函数?

据我所知,第二次调用绑定到第二个函数,因为它是非模板的,尽管两者都是完美的匹配。

如果第一个函数被定义为具有固定数组长度的非模板,则为:

    void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;}

再次在第二个选择中被选中(第二个调用将是不明确的那种方式)。

代码:

template<size_t N> void cm(const char (&h)[N]) 
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)
    {cout << " const char * " << endl;}

int main()
{
    char car[] = "errqweq";
    const char ccar[] = "errqweq";
    cm(car);
    cm(ccar);
}

输出:

 const (&)[N]
 const char * 

2 个答案:

答案 0 :(得分:4)

第一个电话选择功能模板专业化 - 因为它是更好的匹配 让我们标记两个重载:

template<size_t N> void cm(const char (&h)[N])  // (1) - the specialization
    {std::cout << " const (&)[N] " << endl;}

void cm(const char * h)                         // (2)
    {cout << " const char * " << endl;}

对于(1),car绑定到引用。这是身份转换 1 。 对于(2),在car的数组到指针转换后,产生char* 2 ,必须进行限定转换,以便char*成为
char const*。现在正在调用它:

  

标准转换序列S1是一个比转换序列更好的转换序列   标准转换序列S2 if

     
      
  • S1S2的正确子序列(比较13.3.3.1.1定义的规范形式的转换序列,排除任何   左值变换;身份转换序列是   被认为是任何非身份转换的后续行为   序列)或,如果不是,
  •   
  • [...]
  •   

数组到指针的转换是Lvalue转换,所以在这里不予考虑 - 就像在第二个例子中一样。资格转换有一个自己的类别:资格调整。因此,转换为(1)的参数是转换为(2)的参数的子序列:第一个是身份转换,第二个是资格转换,根据上面的段落,身份转换是子序列任何非身份转换。所以选择(1)。

正如您已经提到的那样,在第二种情况下,转换同样很好;由于转换为(2)s参数不是转换为(1)参数的子序列,因此上述引用不起作用。因此,[over.match.best] / 1适用。

  

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

     
      
  • 对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,
  •   
  • 上下文是由用户定义的转换初始化[...],或者,如果不是,
  •   
  • F1是非模板函数,F2是函数模板专业化
  •   

所以(2)选择了一个。如果功能模板不是模板,而是具有参数
char const (&)[8]的函数,则调用将是不明确的as Clang correctly says


1 [over.ics.ref] / 1:

  

当引用类型的参数直接绑定(8.5.3)为a时   参数表达式,隐式转换序列是标识   转换,除非参数表达式的类型是a   派生类的参数类型,在这种情况下是隐式的   转换序列是派生到基础的转换(13.3.3.1)。

[dcl.init.ref] / 5(在8.5.3中):

  

除了最后一个之外的所有情况(即创建和初始化a   暂时从初始化表达式),引用据说   直接绑定到初始化表达式。


2 [conv.array]:

  

N T数组”或“未知数组”的左值或右值   T“的边界可以转换为”指向T的指针“的prvalue。   结果是指向数组的第一个元素的指针。

T可以是cv限定的,指针的类型也是如此。此处T仅为char,因此指针的类型指针指向char =&gt; char*

答案 1 :(得分:2)

因为字符串&#34; errqweq&#34;直接写在代码中是只读的,因为它在运行时是存储器的一部分&#34; protected&#34;因为它是作为常数管理的。

使用const char* ccar;const char ccar[];指向它是正确的。你指的是持有原始&#34; errqweq&#34;使用const说明符:编译器确保不会修改字符串。

但请看:char car[] = "errqweq";

为了给你提供一个可修改的缓冲区(正如你在没有const修饰符的情况下请求的那样),编译器在栈中创建一个包含8个元素(7个字符+ \ 0)的数组,复制它(即:初始化它)字符串&#34; errqweq&#34;

所以第一个调用是使用char buffer[8]个参数,这个参数可以安全地转换为const char buffer[8]。显然,阵列的固定大小与模板最匹配,而不是更多&#34;弱&#34;绑定需要&#34;只是&#34;一个常量指针。