Why does user-defined conversion applied during the initialization? - c++

The following code works fine:
#include <iostream>
struct B
{
operator int()
{
return int();
}
};
struct A
{
A(int, int){ std::cout << "A(int, int)" << std::endl; }
};
A a({B(), B()});
int main()
{
}
and produces output:
A(int, int)
DEMO
But I can't get why? What the Standard says is:
However, when considering the argument of a constructor or
user-defined conversion function that is a candidate by 13.3.1.3 when
invoked for the copying/moving of the temporary in the second step of
a class copy-initialization, by 13.3.1.7 when passing the initializer
list as a single argument or when the initializer list has exactly one
element and a conversion to some class X or reference to (possibly
cv-qualified) X is considered for the first parameter of a constructor
of X [...] only standard conversion sequences and ellipsis conversion sequences are considered
So in our case we considered the argument of the constructor (it was {B(), B()}). More precisely, we passed initializer-list as a single argument (the second case in the rule I cited). Now, we need to convert the first element of the initializer-list (temporary of type B) to int and the only way to do that is to apply user-defined convertion (B::operator int()). But, as said at the end of rule that I cited only standard conversion sequences and ellipsis conversion sequences was considered. Since, that code shouldn't work, it should throw the error like A(int, int) is not viable or sort of.
What's wrong. May be it's a bug?

The wording was defective and changed with C++14. Now [over.best.ics]/4 reads
However, if the target is
the first parameter of a constructor or
[…]
and the constructor or user-defined conversion function is a candidate
by
13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization,
13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases), or
the second phase of 13.3.1.7 when the initializer list has exactly one element, and the target is the first parameter of a
constructor of class X, and the conversion is to X or reference to
(possibly cv-qualified) X,
user-defined conversion sequences are not considered. [ Note: These
rules prevent more than one user-defined conversion from being applied
during overload resolution, thereby avoiding infinite recursion. —
end note ]
The conversion of B() to int is not covered by this - the bold phrase only appertains to the binding of a reference to a temporary during copy-initialization. However, Clang rejects this sample code according to the above:
class A;
struct B
{
operator A();
};
struct A
{
A(A const&){}
};
A a{B()};

Related

What's the rank of implicitly conversion for copy-list-initialization

#include <iostream>
struct A{
A(int){
}
};
struct B{
B() = default;
B(A){
}
B(B const&){}
B(B&&){}
};
int main(){
B b({0});
}
For the given codes, the candidate functions are:
#1 B::B(A)
#2 B::B(const B&)
#3 B::B(B&&)
According to the standard, for #1, the object of type A is copy-list-initialized by {0} as A a = {0}, A::A(int) is considered for the initialization, so only the standard conversion within #1. For #2, it's an initialization of a reference form braced-init-list which is the cause of [dcl.init.list]
Otherwise, if T is a reference type, a prvalue of the type referenced by T is generated. The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind of initialization for the reference. The prvalue is then used to direct-initialize the reference. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type.  — end note ]
So it equates with const B& = {0}, in this initialization, the conversion function is B::B(A) and the argument is 0, so B tmp = {0} and 'B::B(A)' is considered that parameter is initialized by argument 0, as A parameter = 0.
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution...
So there's a user-defined conversion within #2 and the situation of #3 is the same as that of #2 and accroding to the [over.ics.rank],
a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and...
The standard conversion is better than user-defined conversion, so #1 should be better than #2 and #3, but actually, g++ report the invocation is ambiguous, why? The error message is:
main.cpp: In function ‘int main()’:
main.cpp:12:10: error: call of overloaded ‘B(<brace-enclosed initializer list>)’ is ambiguous
B b({0});
^
main.cpp:8:3: note: candidate: B::B(A)
B(A){
^
main.cpp:6:8: note: candidate: constexpr B::B(const B&)
struct B{
^
main.cpp:6:8: note: candidate: constexpr B::B(B&&)
All the three conversions {0} -> A, {0} -> const B&, {0} -> B&& are user-defined conversions.
To convert {0} to A, another overload resolution happens and this time you face three constructors A(int), A(const A&) and A(A&&). Since 0 -> int is a standard conversion while both 0 -> const A& and 0 -> A&& are user-defined conversion, the conversion 0 -> int wins and A(int) is selected to convert {0} to A.
Your confusion comes from mixing the two overload resolutions.
Answers are here,Because the argument is initializer list,so [over.ics.list] rules are performed when overload resolution.
[over.ics.list]/6
Otherwise, if the parameter is a non-aggregate class X and overload resolution per [over.match.list] chooses a single best constructor C of X to perform the initialization of an object of type X from the argument initializer list:
If C is not an initializer-list constructor and the initializer list has a single element of type cv U, where U is X or a class derived from X, the implicit conversion sequence has Exact Match rank if U is X, or Conversion rank if U is derived from X.
Otherwise, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion.
Hence,for these three candiate constructors,the parameter of them are all non-aggerate class type,so the implicit conversion sequence is used to convert the single element of the initializer list to corresponding parameter type of the constructor of parameter of constructor B and therefore these conversion sequences are all user-defined conversion sequence and the second standard conversion sequence are all identity conversions.So the rank of them are indistinguishable.

Double brace initialization

Which constructor should be called in the following code and why?
struct S
{
int i;
S() = default;
S(void *) : i{1} { ; }
};
S s{{}};
If I use clang (from trunk), then the second one is called.
If the second constructor is commented out, then S{{}} is still valid expression, but (I believe) move-constructor from default-constructed instance of S{} is called in the case.
Why conversion constructor has priority over the default one in the very first case?
The intention of such a combination of the constructors of S is to save its std::is_trivially_default_constructible_v< S > property, except a finite set of cases, when it should be initialized in a certain way.
If the second constructor is commented out, then S{{}} is still valid expression, but (I sure) move-constructor from default-constructed instance of S{} is called in the case.
Actually, that's not what happens. The ordering in [dcl.init.list] is:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate class and the initializer list has a single element of type cv U, [...]
— Otherwise, if T is a character array and [...]
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.6.1).
Once you remove the S(void *) constructor, S becomes an aggregate - it has no user-provided constructor. S() = default doesn't count as user-provided because reasons. Aggregate initialization from {} will end up value-initializing the i member.
Why conversion constructor has priority over the default one in the very first case?
With the void* remaining, let's keep going down the bullet list:
— Otherwise, if the initializer list has no elements [...]
— Otherwise, if T is a specialization of std::initializer_list, [...]
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated
and the best one is chosen through overload resolution (13.3, 13.3.1.7).
[over.match.list] gives us a two-phase overload resolution process:
— Initially, the candidate functions are the initializer-list constructors (8.6.4) of the class T and the
argument list consists of the initializer list as a single argument.
— If no viable initializer-list constructor is found, overload resolution is performed again, where the
candidate functions are all the constructors of the class T and the argument list consists of the elements
of the initializer list.
If the initializer list has no elements and T has a default constructor, the first phase is omitted.
S doesn't have any initializer list constructors, so we go into the second bullet and enumerate all the constructors with the argument list of {}. We have multiple viable constructors:
S(S const& );
S(S&& );
S(void *);
The conversion sequences are defined in [over.ics.list]:
Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single
best constructor C of X to perform the initialization of an object of type X from the argument initializer list:
— If C is not an initializer-list constructor and the initializer list has a single element of type cv U, [...]
— Otherwise, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion.
and
Otherwise, if the parameter type is not a class: [...] — if the initializer list has no elements, the implicit conversion sequence is the identity conversion.
That is, the S(S&& ) and S(S const& ) constructors are both user-defined conversion sequences plus identity conversion. But S(void *) is just an identity conversion.
But, [over.best.ics] has this extra rule:
However, if the target is
— the first parameter of a constructor or
— the implicit object parameter of a user-defined conversion function
and the constructor or user-defined conversion function is a candidate by
— 13.3.1.3, when [...]
— 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases), or
— the second phase of 13.3.1.7 when the initializer list has exactly one element that is itself an initializer list, and the target is the first parameter of a constructor of class X, and the conversion is to X or reference to (possibly cv-qualified) X,
user-defined conversion sequences are not considered.
This excludes from consideration S(S const&) and S(S&& ) as candidates - they are precisely this case - the target being the first parameter of the constructor as a result of the second phase of [over.match.list] and the target being a reference to possibly cv-qualified S, and such a conversion sequence would be user-defined.
Hence, the only remaining candidate is S(void *), so it's trivially the best viable candidate.

When are user-defined conversion sequences not considered for selecting viable constructors in C++?

I saw the following words in the C++ standard draft N4582:
[over.best.ics/4] However, if the target is
(4.1) the first parameter of a constructor or
(4.2) the implicit object parameter of a user-defined conversion function
and the constructor or user-defined conversion function is a candidate by
(4.3) 13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization, or
(4.4) 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases),
user-defined conversion sequences are not considered.
I am confused about the bold part, and don't know how to understand it. I write the following program:
#include <iostream>
using namespace std;
struct A {
A(int) {}
operator int() {cout << "user-defined conversion" << endl; return 0;}
A(A&) {} //prevent default copy
};
int main()
{
A a = A(0);
}
It works well in g++ 5.3.0, and output "user-defined conversion", which means a user-defined conversion occurs. Certainly, it can be interpreted as that the temporary A(0) is not a consequence of copy-initialization. Next I change the program to:
#include <iostream>
using namespace std;
struct A {
A(int) {}
operator int() {cout << "user-defined conversion" << endl; return 0;}
A(A&) {} //prevent default copy
};
A foo() {return A(0);}
int main()
{
A a = foo();
}
Now the value of foo() is a temporary copy-initialized from A(0), but the program still works. Why would this happen?
You can go read [dcl.init]/17 for the actual standardese. The "second step" here is referring to copy-initializing a variable of class type A from something b of an unrelated type. In such a case, copy-initialization happens in two steps:
Step 1: you implicitly convert b to A. If you call a converting constructor for this, it creates a temporary A.
Step 2: you then initialize the A variable from the result of the conversion. (In sane classes, this is typically elided.)
What that quote is saying is that you don't do user-defined conversions in this second step.
For example, with your A, A a = 0;. In the first step, you make an A temporary from 0. In the second step, you try to initialize a with that temporary - without using user-defined conversions. That fails, because neither A constructor is viable.
There is only 2 ctors, one that take a reference to an existing object (no temporary) and one that takes an int.
Your code produces (with additional messages):
int ctor
user-defined conversion
int ctor
user-defined conversion
int ctor
The first is the construction inside foo.
The second is due to the fact that the returned value can't be constructed by copy as you prevented it, so the compiler convert the value constructed to an int.
The third is because it uses the ctor that takes an int to build the returned value.
The fourth is (as the second) due to the fact that the returned value can't be used to construct a, so it convert it to an int.
The fifth is because it then uses the ctor that takes an intto build a.

Is it possible to invoke a user-defined conversion function via list-initialization?

Is this program legal?
struct X { X(const X &); };
struct Y { operator X() const; };
int main() {
X{Y{}}; // ?? error
}
After n2672, and as amended by defect 978, 13.3.3.1 [over.best.ics] has:
4 - However, when considering the argument of a constructor or user-defined conversion function that is a candidate [...] by 13.3.1.7 [...] when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X [...], only standard conversion sequences and ellipsis conversion sequences are considered.
This seems rather perverse; it has the result that specifying a conversion using a list-initialization cast is illegal:
void f(X);
f(Y{}); // OK
f(X{Y{}}); // ?? error
As I understand n2640, list-initialization is supposed to be able to replace all uses of direct-initialization and copy-initialization, but there seems no way to construct an object of type X from an object of type Y using only list-initialization:
X x1(Y{}); // OK
X x2 = Y{}; // OK
X x3{Y{}}; // ?? error
Is this the actual intent of the standard; if not, how should it read or be read?
The original intent of 13.3.3.1p4 is to describe how to apply the requirement in 12.3p4 that:
4 - At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
Before defect 84, 13.3.3.1p4 was almost purely informative:
4 - In the context of an initialization by user-defined conversion (i.e., when considering the argument of a user-defined conversion function; see 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv]), only standard conversion sequences and ellipsis conversion sequences are allowed.
This is because 13.3.1.4 paragraph 1 bullet 2 and 13.3.1.5p1b1 restrict the candidate functions to those on class S yielding type T, where S is the class type of the initializer expression and T is the type of the object being initialized, so there is no latitude for another user-defined conversion conversion sequence to be inserted. (13.3.1.4p1b1 is another matter; see below).
Defect 84 repaired the auto_ptr loophole (i.e. auto_ptr<Derived> -> auto_ptr<Base> -> auto_ptr_ref<Base> -> auto_ptr<Base>, via two conversion functions and a converting constructor) by restricting the conversion sequences allowable for the single parameter of the constructor in the second step of class copy-initialization (here the constructor of auto_ptr<Base> taking auto_ptr_ref<Base>, disallowing the use of a conversion function to convert its argument from auto_ptr<Base>):
4 - However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv], or 13.3.1.6 [over.match.ref] in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.
n2672 then adds:
[...] by 13.3.1.7 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class X or reference to (possibly cv-qualified) X is considered for the first parameter of a constructor of X, [...]
This is clearly confused, as the only conversions that are a candidate by 13.3.1.3 and 13.3.1.7 are constructors, not conversion functions. Defect 978 corrects this:
4 - However, when considering the argument of a constructor or user-defined conversion function [...]
This also makes 13.3.1.4p1b1 consistent with 12.3p4, as it otherwise would allow unlimited application of converting constructors in copy-initialization:
struct S { S(int); };
struct T { T(S); };
void f(T);
f(0); // copy-construct T by (convert int to S); error by 12.3p4
The issue is then what the language referring to 13.3.1.7 means. X is being copy or move constructed so the language is excluding applying a user-defined conversion to arrive at its X argument. std::initializer_list has no conversion functions so the language must be intended to apply to something else; if it isn't intended to exclude conversion functions, it must exclude converting constructors:
struct R {};
struct S { S(R); };
struct T { T(const T &); T(S); };
void f(T);
void g(R r) {
f({r});
}
There are two available constructors for the list-initialization; T::T(const T &) and T::T(S). By excluding the copy constructor from consideration (as its argument would need to be converted via a user-defined conversion sequence) we ensure that only the correct T::T(S) constructor is considered. In the absence of this language the list-initialization would be ambiguous. Passing the initializer list as a single argument works similarly:
struct U { U(std::initializer_list<int>); };
struct V { V(const V &); V(U); };
void h(V);
h({{1, 2, 3}});
Edit: and having gone through all that, I've found a discussion by Johannes Schaub that confirms this analysis:
This is intended to factor out the copy constructor for list initialization
because since we are allowed to use nested user defined conversions, we
could always produce an ambiguous second conversion path by first invoking
the copy constructor and then doing the same as we did for the other
conversions.
OK, off to submit a defect report. I'm going to propose splitting up 13.3.3.1p4:
4 - However, when considering the argument of a constructor or user-defined conversion function that is a candidate:
by 13.3.1.3 [over.match.ctor] when invoked for the copying of the temporary in the second step of a class copy-initialization, or
by 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv], or 13.3.1.6 [over.match.ref] in all cases,
only standard conversion sequences and ellipsis conversion sequences are considered; when considering the first argument of a constructor of a class X that is a candidate by 13.3.1.7 [over.match.list] when passing the initializer list as a single argument or when the initializer list has exactly one element, a user-defined conversion to X or reference to (possibly cv-qualified) X is only considered if its user-defined conversion is specified by a conversion function. [Note: because more than one user-defined conversion is allowed in an implicit conversion sequence in the context of list-initialization, this restriction is necessary to ensure that a converting constructor of X, called with a single argument a that is not of type X or a type derived from X, is not ambiguous against a constructor of X called with a temporary X object itself constructed from a. -- end note]
The version of clang 3.1 shipped with XCode 4.4 agrees with your interpretation and rejects X{Y{}};. As do I, after re-reading the relevant parts of the standard a few times, FWIW.
If I modify X's constructor to take two arguments, both of type const X&, clang accepts the statement Y y; X{y,y}. (It crashes if I try X{Y{},Y{}}...). This seems to be consistent with 13.3.3.1p4 which demands user-defined conversions to be skipped only for the single-element case.
It seems that the restriction to standard and ellipsis conversion sequences was added initially only in cases where another user-defined conversion has already taken place. Or at least that is how I read http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#84.
It's interesting how the standard is careful to apply the restriction only to the second step of copy initialization, which copies from a temporary which already has the correct type (and was obtain potentially through a user-defined conversion!). Yet for list-initialization, no similar mechanism seems to exists...

Explicit conversion functions, direct-initialization, and converting constructors

Post-standard draft n3376 has as an example (12.3.2:2) of the use of an explicit conversion function to a user-defined type:
class Y { };
struct Z {
explicit operator Y() const;
};
void h(Z z) {
Y y1(z); // OK: direct-initialization
}
Per 12.3.2:2, an explicit conversion function is "only considered as a user-defined conversion for direct-initialization"; however, that would appear to permit:
struct Y { Y(int); };
struct Z {
explicit operator int() const;
};
void h(Z z) {
Y y1(z); // direct-initialization
}
which appears to conflict with the intent of the standard, and indeed is rejected by gcc-4.7.1:
source.cpp: In function 'void h(Z)':
source.cpp:4:9: error: no matching function for call to 'Y::Y(Z&)'
source.cpp:4:9: note: candidates are:
source.cpp:1:12: note: Y::Y(int)
source.cpp:1:12: note: no known conversion for argument 1 from 'Z' to 'int'
source.cpp:1:8: note: constexpr Y::Y(const Y&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'const Y&'
source.cpp:1:8: note: constexpr Y::Y(Y&&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'Y&&'
Is gcc correct to reject the conversion from Z to Y via int, or does the standard indeed permit this usage?
I considered the context of the mentioned direct-initialization; per the definition of direct-initialization to class type in 8.5:16, a constructor is called with the initializer expression as its arguments, which therefore are converted to the parameter type by an implicit conversion sequence (13.3.3.1). Since an implicit conversion sequence is an implicit conversion (4:3), and thus models copy-initialization (8.5:14) and not direct-initialization, the language in 12.3.2:2 must be referring to the expression as a whole.
Note also that this isn't a violation of 12.3:4 (multiple user-defined conversions); the same compiler is happy with the same code with explicit removed (as are Clang and Comeau):
struct Y { Y(int); };
struct Z { operator int(); };
void h(Z z) {
Y y1(z); // direct-initialization
}
I think Jesse Good has identified the distinction between the operator Y and operator int cases in 13.3.1.4:1, but there's a third case that I'm still concerned by:
struct X {};
struct Y { Y(const X &); };
struct Z {
explicit operator X() const;
};
void h(Z z) {
Y y1(z); // direct-initialization via class-type X
}
The initialization of the temporary X to be bound to the single const X & parameter of the constructor of Y proceeds in a direct-initialization context per 13.3.1.4:1, with T as X and S as Z. I think this clause is incorrect and should read:
13.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy]
1 - [...] When initializing a temporary to be bound to the first parameter
of a constructor that takes a reference to possibly cv-qualified T as its first argument, called with a single argument in the context of direct-initialization of an object of type "cv2 T", explicit conversion functions are also considered. [...]
For the avoidance of confusion, I think 12.3.2:2 should also be amended:
12.3.2 Conversion functions [class.conv.fct]
2 - A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5) in certain contexts (13.3.1.4, 13.3.1.5, 13.3.1.6). [...]
Any comments on the above?
According to 8.5 and 13.3.1.3 the constructors of Y are considered and the best one is picked via overload resolution. In this case the relevant constructors are Y(int); and the copy and move constructors. In the process of overload resolution 13.3.2 Viable functions [over.match.viable] specifies this:
3 Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. [...]
For all those constructors there is no such conversion from Z to either int or one of the flavours of Y. To convince ourselves, let's investigate what the Standard says about implicit conversion sequences in 13.3.3.1 Implicit conversion sequences [over.best.ics]:
1 An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in Clause 4, which means it is governed by the rules for initialization of an object or reference by a single expression (8.5, 8.5.3).
If we cross-reference Clause 4, then we learn that an implicit conversion is defined in terms of copy-initialization (i.e. T t=e;, where T is int and e is z):
(§4.3) An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t (8.5). [...]
So I take 12.3.2:2 not to apply for this initialization, which happens in the larger context of a direct initialization. Doing otherwise would contradict with this latest paragraph.
As noted in Luc Danton's answer, implicit conversion is defined in terms of copy initialization. Then, if we look at 13.3.1.4:1[Copy-initialization of class by user-defined conversion]:
When the type of the initializer expression is a class type “cv S”,
the non-explicit conversion functions of S and its base classes are
considered. When initializing a temporary to be bound to the first
parameter of a constructor that takes a reference to possibly
cv-qualified T as its first argument, called with a single argument in
the context of direct-initialization, explicit conversion functions
are also considered. Those that are not hidden within S and yield a
type whose cv-unqualified version is the same type as T or is a
derived class thereof are candidate functions. Conversion functions
that return “reference to X” return lvalues or xvalues, depending on
the type of reference, of type X and are therefore considered to yield
X for this process of selecting candidate functions.
If I understand this correctly, the first one works because the conversion function yields a Y and is therefore a candidate function as noted by the second emphasized part in the quote, however, in your second case, the set of candidate functions is empty because there is no conversion function to Y and no non-explicit conversion functions as noted by the first emphasized part.
Concerning the third case:
After finding defect report 1087, it seems clear that the intention was to allow, copy, move and template constructors when direct-initializing an object of cv2 T as you mention. If you read the first passage of 13.3.1.4, it says Assuming that “cv1 T” is
the type of the object being initialized, with T a class type, so I think that implies of an object of type "cv2 T" that you mention. However, (after reading it over), it seems that the change due to the defect report has caused the wording to become vague and not cover the third case you propose.
I am no language lawyer, however the wording of the standard implies to me that marking a conversion operator as explicit requires that you explicity specify the conversion type (i.e. int) as part of the initialisation of object y1. With the code Y y1(z), it would appear that you're relying on an implicit conversion, since the type you specify for variable y1 is Y.
Therefore, I would expect correct usage of the explicit conversion operator in this situation to be:
Y y1( int(z) );
Or, since you are effectively specifying a cast, preferably
Y y1( static_cast<int> (z) );