考虑以下计划:
#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
?为什么?
三个编译器的最新发布版本不同意这个问题的答案:
在不同的C ++ 0x草案中,重载决策规则是否发生了重大变化?或者,这两个编译器真的完全错了吗?哪个重载是根据最新的C ++ 0x草案选择的正确重载?
答案 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最后一个子弹获得诊断,因为T1
和T2
与参考相关。如果我们假设临时被第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)