4
struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b({0}); 

我问了一个问题,gcc 和 clang 和 @Johannes Schaub 之间的重载解析得到了不同的结果- litb 解释了有效的规则。但是我对13.3.3.1.4 Reference binding仍有一些疑问。

N4527 13.3.3.1.5 [over.ics.list] p1 和 p8

1 当参数是初始值设定项列表 (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 限定的任何差异都包含在初始化本身中,并且不构成转换。

问题1:“参数表达式”是否包括“初始化列表”?请参阅上面的 13.3.3.1.5 [over.ics.list] p1 粗体短语和

1.3.2 [defns.argument]

争论

<function call expression> 括号内的逗号分隔列表中的表达式 (5.2.2)



8.5 [dcl.init] p17

17 初始化器的语义如下。目标类型是正在初始化的对象或引用的类型,源类型是初始化表达式的类型。如果初始化器不是单个(可能是带括号的)表达式,则未定义源类型。

(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
4

1 回答 1

2

我们只是有一个缺陷,报告为http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1536(我刚刚发现,之前编写时不知道该报告另一个答案)。

继续解释 over.ics.ref 完全与初始化列表无关,并且正在谈论绑定到引用的已创建临时对象(由 decl.init.list 创建的临时对象)对我来说似乎有问题。特别是,over.ics.list 表示 over.ics.ref 将委托给 over.ics.list 以初始化临时对象,这表明 over.ics.ref 在创建临时对象之前已经处于活动状态(也有decl.init.list 没有创建临时文件)。也{ }应该ClassType&是用户定义的转换,但在考虑与初始化列表参数隔离的转换时,临时右值将直接由引用绑定。

于 2015-10-13T12:16:35.280 回答