【发布时间】:2016-01-09 12:25:28
【问题描述】:
struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b({0});
我问了一个问题Overload resolution gets different result between gcc and clang 和@Johannes Schaub - litb 解释了有效的规则。但是我还有一些关于13.3.3.1.4 引用绑定的问题。
N4527 13.3.3.1.5 [over.ics.list] p1 和 p8
1 当 argument 是一个初始化列表 (8.5.4) 时,它不是一个表达式,并且特殊规则适用于转换 它是一个参数类型。
8 否则,如果参数是引用,请参见 13.3.3.1.4。
13.3.3.1.4 [over.ics.ref] p1 和 p2
1 当引用类型的参数直接绑定(8.5.3)到参数表达式时,隐式转换 序列是恒等转换,除非参数表达式的类型是 参数类型,在这种情况下,隐式转换序列是派生到基转换 (13.3.3.1)。 [示例...]
如果参数直接绑定到应用转换函数的结果 参数表达式,隐式转换序列是用户定义的转换序列(13.3.3.1.2), 使用第二个标准转换序列进行身份转换,或者,如果转换函数 返回一个类型的实体,该类型是参数类型的派生类,即派生到基的转换。
2 当引用类型的参数不直接绑定到参数表达式时,转换顺序 是将参数表达式转换为引用的基础类型所需的一种 到 13.3.3.1。从概念上讲,这个转换序列对应于复制初始化一个临时的 带有参数表达式的基础类型。顶级 cv-qualification 的任何差异都包含在 初始化本身并不构成转换。
问题1:“参数表达式”是否包含“初始化列表”?请参阅上面的 13.3.3.1.5 [over.ics.list] p1 粗体短语和
1.3.2 [defns.argument]
论据
括号内的逗号分隔列表中的表达式 (5.2.2)
8.5 [dcl.init] p17
17 初始化器的语义如下。目标类型是对象或引用的类型 initialized 并且源类型是初始化表达式的类型。如果初始化器不是单个(可能 括号) 表达式,源类型未定义。
(17.1) — 如果初始化器是(非括号)大括号初始化列表,则对象或引用是列表初始化的 (8.5.4)。
(17.2) — 如果目标类型是引用类型,请参阅 8.5.3。
8.5.3 [dcl.init.ref] p5
对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化,如下所示:
[...]
(5.2.2.2) — 否则,将创建“cv1 T1”类型的临时变量并从初始化程序复制初始化 (8.5) 表达。然后将引用绑定到临时对象。
[...]
在除最后一种情况外的所有情况下(即从初始化表达式创建和初始化一个临时值), 据说引用直接绑定到初始化表达式。
问题2:“直接绑定”是否包括初始化器为初始化器列表的情况?也就是说,当初始化器是初始化器列表时,我们可以使用“直接绑定”吗?
注意:“直接绑定”是 8.5.3 中的定义,由 8.5 p17.1 引用,“初始化程序是一个花括号初始化列表”是 8.5.4 中的定义,由 8.5 p17.2 引用
//case 5.2.1.2
struct X{};
struct Y{Y(X);};
const Y& y1 = X(); // bind directly
const Y& y2 = {X()}; // bind directly or not?
struct Z{operator X();};
const X& x1 = Z(); // bind directly
const X& x2 = {Z()}; // bind directly or not?
//case 5.2.2.1
struct A{operator int();};
const int& a1 = A(); // bind directly
const int& a2 = {A()}; // bind directly or not?
struct B{B(int);};
const B& b1 = 1; // bind directly
const B& b2 = {1}; // bind directly or not?
//csse 5.2.2.2
int i3 = 2;
double&& rrd3 = i3; // not bind directly
struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b({0}); // when overload resolution choose B(const B&) as a candidate,
// {0} -> constB& bind directly or not?
问题 3(主要问题):
当参数是初始化列表并且参数是引用时,13.3.3.1.5 [over.ics.list] p8 引用 13.3.3.1.4 [over.ics.ref],但我不能查看关于参数的任何单词,它是一个初始化列表。我认为“直接绑定”和“参数”的定义与“初始化列表”无关。
你能解释一下当参数是初始化列表并且参数是引用时重载解析是如何工作的吗?
注意:这三个问题相关。当您回答第三个问题时,您将回答第一个和第二个问题。
struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b1(0); //when overload resolution choose B(const B&) as a candidate,
//0 -> const B& binds directly
//13.3.3.1.4 [over.ics.ref] p1 "If the parameter binds directly..."
A a;
B b2(a) //when overload resolution choose B(const B&) as a candidate,
//a -> const B& binds directly
//13.3.3.1.4 [over.ics.ref] p1 "If the parameter binds directly..."
B b3({0})//when overload resolution choose B(const B&) as a candidate,
//{0} -> const B& binds directly or not?
//if it is not bound directly, 13.3.3.1.4 [over.ics.ref] p2
B b3({a})//when overload resolution choose B(const B&) as a candidate,
//{a} -> const B& binds directly or not?
//if it is not bound directly, 13.3.3.1.4 [over.ics.ref] p2
【问题讨论】:
-
列表初始化在[decl.init.list] 中定义,它不被称为“参数表达式”(这是有道理的,因为它不是表达式)。此外,“直接绑定”意味着类型是已知的。大括号初始化器没有类型。
-
@0x499602D2 当参数是 13.3.3.1.4 [over.ics.ref] 中的初始值设定项列表时,适用哪个规则?第一个和第二个都适用于“参数表达式”,它们使用“直接绑定”来区分。
-
参见"Otherwise, if
Tis a reference type..." 这是涵盖从初始化列表初始化引用的规则。 -
OTOH,我在上面链接到的规则 ([dcl.init.list]/3.8) 说创建了一个临时文件,并且引用绑定到该临时文件;所以这个临时可能是 [over.ics.ref] 所指的“参数表达式”。
-
@0x499602D2 这个问题你可以回答,我接受。
标签: c++ c++11 language-lawyer overload-resolution list-initialization