stackcpp
stackcpp

Reputation: 1285

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

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

I have asked a question Overload resolution gets different result between gcc and clang and @Johannes Schaub - litb explained the rules that are active. But I still have some questions about 13.3.3.1.4 Reference binding.

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

1 When an argument is an initializer list (8.5.4), it is not an expression and special rules apply for converting it to a parameter type.

8 Otherwise, if the parameter is a reference, see 13.3.3.1.4.

13.3.3.1.4 [over.ics.ref] p1 and p2

1 When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1). [ Example... ]

If the parameter binds directly to the result of applying a conversion function to the argument expression, the implicit conversion sequence is a user-defined conversion sequence (13.3.3.1.2), with the second standard conversion sequence either an identity conversion or, if the conversion function returns an entity of a type that is a derived class of the parameter type, a derived-to-base Conversion.

2 When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the underlying type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization itself and does not constitute a conversion.

Question 1: Does "argument expression" include "initializer list"? See 13.3.3.1.5 [over.ics.list] p1 bold phrase above and

1.3.2 [defns.argument]

argument

<function call expression> expression in the comma-separated list bounded by the parentheses (5.2.2)



8.5 [dcl.init] p17

17 The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.

(17.1) — If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).

(17.2) — If the destination type is a reference type, see 8.5.3.

8.5.3 [dcl.init.ref] p5

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

[...]

(5.2.2.2) — Otherwise, a temporary of type “cv1 T1” is created and copy-initialized (8.5) from the initializer expression. The reference is then bound to the temporary.

[...]

In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.

Question 2: Does the "bind directly" include the case in which the initializer is an initializer list? In other words, can we use "bind directly" when the initializer is an initializer list?

NOTE: "bind directly" is definition in 8.5.3 which is quoted by 8.5 p17.1 and "initializer is a braced-init-list" is definition in 8.5.4 which is quoted by 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? 


Question 3(the main question):

when an argument is an initializer list and the parameter is a reference, 13.3.3.1.5 [over.ics.list] p8 quotes to 13.3.3.1.4 [over.ics.ref], but I can't see any words about argument which is an initializer list. I think the definition of "bind directly" and "argument" is not related with "initializer list".

Can you explain how overload resolution work when an argument is an initializer list and the parameter is a reference?

NOTE: These three questions are related. When you answer the third question, you will answer the first and second.

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

Upvotes: 4

Views: 219

Answers (1)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506847

We simply have a defect, which was reported as http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1536 (I just found that, were not aware of that report previously when writing the other answer).

Going with the interpretation that over.ics.ref is completely initializer list agnostic and is talking about the created temporaries' (the one created by decl.init.list) binding to the reference seems problematic to me. In particular, over.ics.list says that over.ics.ref will delegate to over.ics.list for initiapization of the temporary, indicating that over.ics.ref is active already before creation of the temporary (also there are cases in decl.init.list where no temporary is created). Also { } to ClassType& should be a user defined conversion but the temporary rvalue will be bound directly by the refefence when considering the conversion isolated from the initializer list argument.

Upvotes: 2

Related Questions