重载分辨率和数组:应该调用哪个函数?

时间:2011-03-18 02:33:18

标签: c++ arrays reference c++11 overload-resolution

考虑以下计划:

#include <cstddef>
#include <cstdio>

void f(char const*&&)      { std::puts("char const*&&");      } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)

template <std::size_t N>
void f(char const (&)[N])  { std::puts("char const(&)[N]");   } // (3)

int main()
{
    const char data[] = "a";
    f(data);
}

应该调用哪个f?为什么?

三个编译器的最新发布版本不同意这个问题的答案:

    使用 g ++ 4.5.2 编译程序时,会调用
  • (1) 使用 Visual C ++ 2010 SP1
  • 编译程序时,会调用
  • (2) 使用 Clang 3.0(主干127530)
  • 编译程序时,会调用
  • (3)

在不同的C ++ 0x草案中,重载决策规则是否发生了重大变化?或者,这两个编译器真的完全错了吗?哪个重载是根据最新的C ++ 0x草案选择的正确重载?

3 个答案:

答案 0 :(得分:12)

首先,所有三个的转换序列是相同的,除了对于前两个,存在左值变换(左值到左值转换),但是它不用于排序转换序列。这三个都是完全匹配(函数模板特化具有参数类型char const(&)[2])。

如果您在13.3.3.2p3处迭代规则,则停在此段

  

S1和S2是引用绑定(8.5.3),并且都没有引用没有ref-qualifier声明的非静态成员函数的隐式对象参数,S1将rvalue引用绑定到rvalue,S2绑定左值参考

如果需要将左值引用绑定到左值,则无法形成转换序列,规范在13.3.3.1.4p3处说明。如果你看一下8.5.3p5最后一个项目符号中引用绑定是如何工作的,它将从数组lvalue创建一个类型char const*的临时(我认为它们意味着 rvalue 临时)并绑定引用到那个临时的。因此,我认为(1)优于(2)。同样适用于(1) (3),虽然我们不需要这个,因为(3)是一个模板,所以在平局中,我们会再次选择(1)

n3225中,他们更改了引用绑定规则,以便rvalue引用可以绑定到lvalues的初始化表达式,只要引用绑定到rvalue(可能通过在之前正确转换初始化程序而创建) 。这可能会影响Visual C ++的处理,这可能不是最新的。

我不确定铿锵。即使它会忽略(1),它也会在(2)(3)之间形成一个平局,并且需要选择(2),因为它是非模板。< / p>


我认为8.5.3p5最后一颗子弹令人困惑,因为它说“其他类型是临时的......”。目前尚不清楚临时是否被认为是左值或13.3.3.1.4p3的右值,这意味着我不确定以下内容应该如何根据规范的确切词语真正表现

void f(int &);
void f(int &&);

int main() {
  int n = 0;
  f(n);
}

如果我们假设临时被第13条视为rvalue,那么我们将rvalue ref绑定到第二个函数中的rvalue和第一个函数中的左值。因此,我们将选择第二个函数,然后通过8.5.3p5最后一个子弹获得诊断,因为T1T2与参考相关。如果我们假设临时被第13条视为左值,那么以下内容将不起作用

void f(int &&);
int main() {
  f(0);
}

因为我们将rvalue ref绑定到左值,而第13条将使该函数不可行。如果我们解释“将rvalue ref绑定到左值”来引用初始化表达式而不是绑定的最终表达式,我们将不接受以下内容

void f(float &&);
int main() {
  int n = 0;
  f(n);
}

但是从n3225开始有效。所以似乎有些混乱 - 我向委员会发送了一份关于此问题的DR。

答案 1 :(得分:4)

我声称#3是由符合标准的编译器选择的函数。

(1)优于(2)因为“标准转换序列S1是比标准转换序列S2更好的转换序列,如果S1和S2是引用绑定(8.5.3)并且都不是指隐含的对象参数在没有ref-quali fi er的情况下声明的非静态成员函数,S1将rvalue引用绑定到rvalue,S2绑定左值引用。“

(3)优于(1)和(2),因为它是身份转换(其他是精确匹配转换)和“标准转换序列S1是比标准转换序列S2更好的转换序列,如果S1是S2的正确子序列(比较13.3.3.1.1定义的规范形式的转换序列,不包括任何左值变换;身份转换序列被认为是任何非同一性转换序列的子序列)“

模板与非模板仅在转换效果较好时才考虑“或者,如果不是......”

但奇怪的是,Comeau更喜欢(2)超过(3)。此测试用例无法编译:

#include <cstddef>
#include <cstdio>

// (1) removed because Comeau doesn't support rvalue-references yet
char f(char const* const&) { std::puts("char const* const&"); return 0; } // (2)

template <std::size_t N>
int f(char const (&)[N])  { std::puts("char const(&)[N]"); return 0; } // (3)

int main()
{
    const char data[] = "a";
    switch (0) {
       case sizeof(char):
           break;
       case sizeof(f(data)):
           break;
    }
}

答案 2 :(得分:2)