当实参是初始化列表而形参是引用时,重载解析

Overload resolution when an argument is an initializer list and the parameter is a reference

本文关键字:重载 引用 形参 实参 初始化 列表      更新时间:2023-10-16
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引用绑定还有一些疑问。

N4527 13.3.3.1.5 [over.ics.]

实参是初始化列表(8.5.4)时,不是表达式,并且应用特殊规则进行转换

8否则,如果是引用,请参见13.3.3.1.4。

13.3.3.1.4 [over.ics。Ref] p

当引用类型的形参直接将(8.5.3)绑定到实参表达式时,进行隐式转换类的派生类的类型除外,否则参数表达式为恒等转换在这种情况下,隐式转换序列是派生到基的转换(13.3.3.1)。(例子…)

如果形参直接绑定到对对象应用转换函数的结果参数表达式时,隐式转换序列为用户定义的转换序列(13.3.3.1.2);对于第二个标准转换序列,要么是单位转换,要么是转换函数返回类型为参数类型的派生类的实体,即派生到基的转换。

2当引用类型的形参没有直接绑定实参表达式时,转换顺序是否需要将参数表达式转换为引用的基础类型13.3.3.1。从概念上讲,此转换序列对应于复制初始化类的临时对象使用参数表达式的基础类型。顶级简历资格的任何差异都包含在初始化本身,不构成转换。

问题1:"参数表达式"是否包含"初始化列表"?参见13.3.3.1.5 [over.ics]。

1.3.2 [defns.argument]

论证

<函数调用表达式>表达式在以逗号分隔的圆括号括起的列表中(5.2.2)



(dcl

8.5。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:"直接绑定"是否包括初始化项是初始化项列表的情况?换句话说,当初始化项是初始化项列表时,我们可以直接使用"bind"吗?

注意:"bind direct "是8.5.3中的定义,被8.5 p17.1引用;"initializer is a braced-init-list"是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]。参考13.3.3.1.4 [over.ics.]ref],但是我没有看到任何关于argument的文字,它是一个初始化列表。我认为"直接绑定"answers"参数"的定义与"初始化列表"无关。

你能解释一下当实参是初始化列表而形参是引用时,重载解析是如何工作的吗?

注意:这三个问题与相关。当你回答第三个问题时,你将回答第一个和第二个问题。

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

我们只是有一个缺陷,它被报告为http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1536(我只是发现,在编写其他答案时,我们之前没有意识到该报告)。

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