30

考虑以下程序:

#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应该叫哪个?为什么?

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

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

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

4

3 回答 3

12

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

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

S1 和 S2 是引用绑定 (8.5.3),两者都没有引用声明的非静态成员函数的隐式对象参数,而 S1 将右值引用绑定到右值,S2 绑定左值引用。

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

n3225中,他们更改了引用绑定规则,以便右值引用可以绑定到作为左值的初始值设定项表达式,只要引用将绑定到右值(可能通过之前正确转换初始值设定项来创建)。这可能会影响 Visual C++ 的处理,此处可能不是最新的。

我不确定clang。即使它会忽略(1),它也会最终处于和 之间(2)(3)并且需要选择(2),因为它是非模板。


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

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

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

如果我们假设第 13 条将临时值视为右值,那么我们将右值 ref 绑定到第二个函数中的右值和第一个函数中的左值。因此,我们将选择第二个函数,然后通过 8.5.3p5 最后一个项目符号获得诊断,因为T1和 与T2引用相关。如果我们假设第 13 条将临时值视为左值,那么以下内容将不起作用

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

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

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

然而,这从 n3225 开始有效。所以似乎有些混乱 - 我就此向委员会发送了 DR。

于 2011-03-18T12:53:19.130 回答
4

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

(1) 比 (2) 好,因为“如果 S1 和 S2 是引用绑定 (8.5.3) 并且两者都没有引用非静态对象的隐式对象参数,则标准转换序列 S1 是比标准转换序列 S2 更好的转换序列没有引用限定符声明的成员函数,S1 将右值引用绑定到右值,S2 绑定左值引用。”

(3) 比 (1) 和 (2) 都好,因为它是恒等转换(其他是完全匹配转换),并且“如果 S1 是适当的子序列,则标准转换序列 S1 比标准转换序列 S2 更好的转换序列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;
    }
}
于 2011-03-18T03:16:17.613 回答
2

这是一个社区 wiki 答案,用于从标准中收集片段(草案 3225)。

第 13.3.3 节“最佳可行功能”[over.match.best]

  1. 定义 ICSi(F) 如下:

    • 如果F是一个静态成员函数,ICS1(F) 被定义为对于任何函数 ICS1(F) 既不比 ICS1(G) 好也不差G,并且对称地,ICS1(G) 既不比 ICS1(F) 好也不差); 除此以外,

    • 令 ICSi(F) 表示将列表中的第 i 个参数转换为可行函数的第 i 个参数的类型的隐式转换序列F。13.3.3.1 定义了隐式转换序列,13.3.3.2 定义了一个隐式转换序列比另一个更好或更差的转换序列意味着什么。

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

    • 对于某些参数j, ICSj(F1) 是比 ICSj(F2) 更好的转换序列

    或者,如果不是这样,

    • 上下文是通过用户定义的转换(参见 8.5、13.3.1.5 和 13.3.1.6)F1进行的初始化,并且从从返回类型F2到目标类型的转换序列比标准转换序列更好

    或者,如果不是这样,

    • F1是一个非模板函数并且F2是一个函数模板特化

    或者,如果不是这样,

    • F1F2是函数模板特化,根据 14.5.6.2 中描述的部分排序规则,函数模板 forF1比模板 for 更特化。F2
  2. 如果恰好有一个可行函数比所有其他可行函数更好,那么它就是重载决议选择的那个;否则调用格式不正确。

第 13.3.3.1.4 节“参考绑定”[over.ics.ref]

  1. 当引用类型的参数直接(8.5.3)绑定到参数表达式时,隐式转换序列是恒等转换,除非参数表达式的类型是参数类型的派生类,在这种情况下,隐式转换序列是派生到基础的转换 (13.3.3.1)。如果参数直接绑定到将转换函数应用于参数表达式的结果,则隐式转换序列是用户定义的转换序列 (13.3.3.1.2),第二个标准转换序列是恒等转换,或者,如果转换函数返回一个类型的实体,该类型是参数类型的派生类,即派生到基转换。

  2. 当引用类型的参数未直接绑定到参数表达式时,转换顺序是根据 13.3.3.1 将参数表达式转换为引用的基础类型所需的顺序。从概念上讲,此转换序列对应于使用参数表达式复制初始化基础类型的临时变量。顶级 cv 限定的任何差异都包含在初始化本身中,并且不构成转换。

第 13.3.3.2 节“对隐式转换序列进行排名”[over.ics.rank]

  1. 13.3.3.2 基于更好的转换序列和更好的转换之间的关系定义了隐式转换序列的部分排序。如果这些规则将隐式转换序列S1定义为比 更好的转换序列S2,那么它也是S2比 更差的转换序列S1。如果转换序列S1既不优于也不差于转换序列S2,则称S1S2为不可区分的转换序列。

  2. 在比较隐式转换序列的基本形式时(如 13.3.3.1 中所定义)

    • 标准转换序列 (13.3.3.1.1) 是比用户定义的转换序列或省略号转换序列更好的转换序列,并且

    • 用户定义的转换序列 (13.3.3.1.2) 是比省略号转换序列 (13.3.3.1.3) 更好的转换序列。

  3. 除非以下规则之一适用,否则相同形式的两个隐式转换序列是无法区分的转换序列:

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

      • S1是S2的真子序列(比较13.3.3.1.1定义的规范形式的转换序列,不包括任何左值转换;恒等转换序列被认为是任何非恒等转换序列的子序列)

      或者,如果不是这样,

      • 的排名S1优于的排名S2S1S2

      或者,如果不是这样,

      • S1并且S2仅在它们的限定转换和产生相似的类型T1T2(4.4) 方面有所不同,并且 type 的 cv 限定签名是 type 的 cv 限定签名T1的适当子集T2

      或者,如果不是这样,

      • S1S2是引用绑定(8.5.3),既不引用声明的非静态成员函数的隐式对象参数,也没有引用限定符,并且S1将右值引用绑定到右值并S2绑定左值引用。
于 2011-03-18T02:52:29.133 回答