Discussion:
Revisiting core issue 1758 (explicit conversion in list-initialization)
(too old to reply)
T. C.
2017-05-15 20:08:14 UTC
Permalink
Core issue 1758 points out that under C++14-as-published the following code
is ill-formed:

struct X { X(); };
struct Y { explicit operator X(); } y;
X x{y};


because (among other things) X x{y}; uses [over.match.list] via N4140
[dcl.init.list]/3.4 (3.6 in the current WP), which makes no special
allowance for explicit conversion functions.

Core issue 1467's resolution supposedly resolved this, but I'm not seeing
it. The "(by copy-initialization for copy-list-initialization, or by
direct-initialization for direct-list-initialization)" it added to what is
now 3.8 doesn't apply to class types, and I don't see anything else in the
resolution that could affect this case.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2017-05-15 21:41:36 UTC
Permalink
Post by T. C.
Core issue 1758 points out that under C++14-as-published the following code
struct X { X(); };
struct Y { explicit operator X(); } y;
X x{y};
because (among other things) X x{y}; uses [over.match.list] via N4140
[dcl.init.list]/3.4 (3.6 in the current WP), which makes no special
allowance for explicit conversion functions.
Core issue 1467's resolution supposedly resolved this, but I'm not seeing
it. The "(by copy-initialization for copy-list-initialization, or by
direct-initialization for direct-list-initialization)" it added to what is
now 3.8 doesn't apply to class types, and I don't see anything else in the
resolution that could affect this case.
Why does it not apply to class types (note that a follow-up issue
resolution changed that in turn to "aggregate class")? But I don't
think it matters anymore, since I suspect that they figured out that
core issues 1758's statement: "First, the special provision allowing
an explicit conversion function to be used when initializing the
parameter of a copy/move constructor is in 13.3.1.4 [over.match.copy],
and this case takes us to 13.3.1.7 [over.match.list] instead." is
incorrect (or some other issue resolution changed something else into
the status quo), so they did nothing for that. Because we are not only
taken to 13.3.1.7 but also to 13.3.1.4: We will be copy-initializing
(non-list!) the reference parameter of X's copy/move constructor, by
doing overload resolution for an overload resolution context of
13.3.1.7. The copy-initialization of the parameters of the candidates
of that outer context however is done by 13.3.1.4, and that paragraph
simply says "and the constructor is called with a single argument in
the context of direct-initialization". A direct-list-initialization is
a form of direct-initialization, so everything is fine.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-05-15 21:54:40 UTC
Permalink
Post by T. C.
Post by T. C.
Core issue 1758 points out that under C++14-as-published the following
code
Post by T. C.
struct X { X(); };
struct Y { explicit operator X(); } y;
X x{y};
because (among other things) X x{y}; uses [over.match.list] via N4140
[dcl.init.list]/3.4 (3.6 in the current WP), which makes no special
allowance for explicit conversion functions.
Core issue 1467's resolution supposedly resolved this, but I'm not
seeing
Post by T. C.
it. The "(by copy-initialization for copy-list-initialization, or by
direct-initialization for direct-list-initialization)" it added to what
is
Post by T. C.
now 3.8 doesn't apply to class types, and I don't see anything else in
the
Post by T. C.
resolution that could affect this case.
Why does it not apply to class types (note that a follow-up issue
resolution changed that in turn to "aggregate class")?
There are two copies of that parenthetical. The first doesn't apply because
there's no derivation relationship between X and Y. The second doesn't
apply because it's after the "if T is a class type..." bullet.
Post by T. C.
But I don't
think it matters anymore, since I suspect that they figured out that
core issues 1758's statement: "First, the special provision allowing
an explicit conversion function to be used when initializing the
parameter of a copy/move constructor is in 13.3.1.4 [over.match.copy],
and this case takes us to 13.3.1.7 [over.match.list] instead." is
incorrect (or some other issue resolution changed something else into
the status quo), so they did nothing for that. Because we are not only
taken to 13.3.1.7 but also to 13.3.1.4: We will be copy-initializing
(non-list!) the reference parameter of X's copy/move constructor, by
doing overload resolution for an overload resolution context of
13.3.1.7. The copy-initialization of the parameters of the candidates
of that outer context however is done by 13.3.1.4, and that paragraph
Post by T. C.
simply says "and the constructor is called with a single argument in
the context of direct-initialization".
Let's suppose overload resolution managed to (correctly) pick the move
constructor. The parameter type is a reference,
so you don't go to [over.match.copy]; you goes to [over.match.ref] via
[dcl.init.ref]. Since you are copy-initializing the reference,
the "for direct initialization" part doesn't apply.
Post by T. C.
A direct-list-initialization is
a form of direct-initialization, so everything is fine.
You need to somehow reach [over.match.copy] first. I don't see it.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2017-05-15 22:46:27 UTC
Permalink
Post by T. C.
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
Post by T. C.
Core issue 1758 points out that under C++14-as-published the following code
struct X { X(); };
struct Y { explicit operator X(); } y;
X x{y};
because (among other things) X x{y}; uses [over.match.list] via N4140
[dcl.init.list]/3.4 (3.6 in the current WP), which makes no special
allowance for explicit conversion functions.
Core issue 1467's resolution supposedly resolved this, but I'm not seeing
it. The "(by copy-initialization for copy-list-initialization, or by
direct-initialization for direct-list-initialization)" it added to what is
now 3.8 doesn't apply to class types, and I don't see anything else in the
resolution that could affect this case.
Why does it not apply to class types (note that a follow-up issue
resolution changed that in turn to "aggregate class")?
There are two copies of that parenthetical. The first doesn't apply because
there's no derivation relationship between X and Y. The second doesn't apply
because it's after the "if T is a class type..." bullet.
I agree, I was confused.
Post by T. C.
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
But I don't
think it matters anymore, since I suspect that they figured out that
core issues 1758's statement: "First, the special provision allowing
an explicit conversion function to be used when initializing the
parameter of a copy/move constructor is in 13.3.1.4 [over.match.copy],
and this case takes us to 13.3.1.7 [over.match.list] instead." is
incorrect (or some other issue resolution changed something else into
the status quo), so they did nothing for that. Because we are not only
taken to 13.3.1.7 but also to 13.3.1.4: We will be copy-initializing
(non-list!) the reference parameter of X's copy/move constructor, by
doing overload resolution for an overload resolution context of
13.3.1.7. The copy-initialization of the parameters of the candidates
of that outer context however is done by 13.3.1.4, and that paragraph
simply says "and the constructor is called with a single argument in
the context of direct-initialization".
Let's suppose overload resolution managed to (correctly) pick the move
constructor. The parameter type is a reference,
so you don't go to [over.match.copy]; you goes to [over.match.ref] via
[dcl.init.ref]. Since you are copy-initializing the reference,
the "for direct initialization" part doesn't apply.
It's a bit convoluted, but I still think it works. Clause dcl.init.ref
says "and can be converted to an rvalue ..", however this case is
intended for conversion functions that return rvalue references (see
[over.match.ref] which restricts enumeration only to those).

So after over.match.list, we are taken to over.ics.ref, since a
reference parameter is involved. The case "If the parameter binds
directly to the result of applying a conversion function " seems not
to be the case (since our conversion function return is not a
reference type, so reference initialization classifies it as "If T1 or
T2 is a class type and T1 is not reference-related to T2 ..." in
dcl.init.ref, which (I think) is not direct initialization). So
over.ics.ref says "
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 referenced type according to
[over.best.ics]. Conceptually, this conversion sequence corresponds to
copy-initializing a temporary of the referenced type with the argument
expression.".

So we need to convert Y to X. Now we have the plain copy
initialization scenario. I.e [dcl.init]'s "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 over.match.copy says "When initializing a temporary to be bound to
the first parameter of a constructor .. and the constructor is called
with a single argument in the context of direct-initialization". Note
that we initialize a temporary that is needed by the reference
initialization, and it will be bound to the first parameter of the
constructor, which was a candidate of [over.match.list]. So I think
everything will work fine. Note that it does not test the
direct-initialization property of the reference initalization, but it
tests the direct-initialization property of the context whichever gave
rise to the constructor to be called, which is [dcl.init.list] ->
[over.match.list].
Post by T. C.
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
A direct-list-initialization is
a form of direct-initialization, so everything is fine.
You need to somehow reach [over.match.copy] first. I don't see it.
I hope the above will work.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2017-05-15 22:56:29 UTC
Permalink
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
Post by T. C.
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
But I don't
think it matters anymore, since I suspect that they figured out that
core issues 1758's statement: "First, the special provision allowing
an explicit conversion function to be used when initializing the
parameter of a copy/move constructor is in 13.3.1.4 [over.match.copy],
and this case takes us to 13.3.1.7 [over.match.list] instead." is
incorrect (or some other issue resolution changed something else into
the status quo), so they did nothing for that. Because we are not only
taken to 13.3.1.7 but also to 13.3.1.4: We will be copy-initializing
(non-list!) the reference parameter of X's copy/move constructor, by
doing overload resolution for an overload resolution context of
13.3.1.7. The copy-initialization of the parameters of the candidates
of that outer context however is done by 13.3.1.4, and that paragraph
simply says "and the constructor is called with a single argument in
the context of direct-initialization".
Let's suppose overload resolution managed to (correctly) pick the move
constructor. The parameter type is a reference,
so you don't go to [over.match.copy]; you goes to [over.match.ref] via
[dcl.init.ref]. Since you are copy-initializing the reference,
the "for direct initialization" part doesn't apply.
It's a bit convoluted, but I still think it works. Clause dcl.init.ref
says "and can be converted to an rvalue ..", however this case is
intended for conversion functions that return rvalue references (see
[over.match.ref] which restricts enumeration only to those).
I have an error here (which has no effect on the outcome though) -
actually when initializing an rvalue reference, conversion functions
that return prvalues are enumerated aswell. That got me, since in
C++03 for direct binding you had to always return lvalue references
from conversion functions.

However that doesn't change much of my explanation, because only
non-explicit conversion functions are enumerated: So Y can still not
be bound directly to the reference parameter.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-05-15 23:09:38 UTC
Permalink
[over.ics.ref] is about computing implicit conversion sequences for
overload resolution purposes, not actual initialization semantics, which is
controlled by [dcl.init] and its subclauses. Whatever [over.ics.ref] has to
say is irrelevant once the constructor overload is chosen. So, assuming for
the sake of argument that [over.ics.ref] says an ICS from Y to X&& can be
formed in this context, you've established that the move constructor of X
is deemed viable by overload resolution for the initialization X x{y}; and
will eventually get chosen by it. That doesn't mean that the subsequent
initialization will then be well-formed; at most it suggests that it is
*intended* to be well-formed.

The actual call requires copy-initializing the X&& parameter with a Y.
That, according to [dcl.init], goes to [dcl.init.ref], which goes to
[over.match.ref], which fails because we are copy-initializing rather than
direct-initializing the reference.

(Note that there's also a separate problem here: the current wording
requires a temporary to be materialized for binding to the parameter and
therefore prevents copy elision.)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2017-05-15 23:15:19 UTC
Permalink
[over.ics.ref] is about computing implicit conversion sequences for overload
resolution purposes, not actual initialization semantics, which is
controlled by [dcl.init] and its subclauses. Whatever [over.ics.ref] has to
say is irrelevant once the constructor overload is chosen. So, assuming for
the sake of argument that [over.ics.ref] says an ICS from Y to X&& can be
formed in this context, you've established that the move constructor of X is
deemed viable by overload resolution for the initialization X x{y}; and will
eventually get chosen by it. That doesn't mean that the subsequent
initialization will then be well-formed; at most it suggests that it is
*intended* to be well-formed.
I agree.
The actual call requires copy-initializing the X&& parameter with a Y. That,
according to [dcl.init], goes to [dcl.init.ref], which goes to
[over.match.ref], which fails because we are copy-initializing rather than
direct-initializing the reference.
I disagree with "which goes to [over.match.ref]", because it fails.
So, because it fails, we cannot "y" to an rvalue of "X". Therefore we
need to continue the bullets of dcl.init.ref and eventually we end up
copy-initializing a X from y.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-05-15 23:29:56 UTC
Permalink
Post by 'Johannes Schaub' via ISO C++ Standard - Discussion
Post by T. C.
[over.ics.ref] is about computing implicit conversion sequences for
overload
Post by T. C.
resolution purposes, not actual initialization semantics, which is
controlled by [dcl.init] and its subclauses. Whatever [over.ics.ref] has
to
Post by T. C.
say is irrelevant once the constructor overload is chosen. So, assuming
for
Post by T. C.
the sake of argument that [over.ics.ref] says an ICS from Y to X&& can
be
Post by T. C.
formed in this context, you've established that the move constructor of
X is
Post by T. C.
deemed viable by overload resolution for the initialization X x{y}; and
will
Post by T. C.
eventually get chosen by it. That doesn't mean that the subsequent
initialization will then be well-formed; at most it suggests that it is
*intended* to be well-formed.
I agree.
Post by T. C.
The actual call requires copy-initializing the X&& parameter with a Y.
That,
Post by T. C.
according to [dcl.init], goes to [dcl.init.ref], which goes to
[over.match.ref], which fails because we are copy-initializing rather
than
Post by T. C.
direct-initializing the reference.
I disagree with "which goes to [over.match.ref]", because it fails.
So, because it fails, we cannot "y" to an rvalue of "X". Therefore we
need to continue the bullets of dcl.init.ref and eventually we end up
copy-initializing a X from y.
You are right, we fall through to 5.2.2.1, which says "user-defined
conversions are considered using the rules for copy-initialization of an
object of type [X] by user-defined conversion". But now we are dealing with
copy-initializing an X from y, not direct-initializing one. I'm not seeing
where it says that this hypothetical copy-initialization retains the
direct-initialization "tag" of the initialization two times removed, so to
speak.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-05-16 02:05:42 UTC
Permalink
OK, I just read through [dcl.init]/17 again. I can't see any distinction
between what it says for the direct-non-list-init case and the
direct-list-init case. After overload resolution, the actual reference
initialization is subject to the same set of rules, so if it works in one
it must work in the other, and the wording at issue is clearly intended to
work at least for the direct-non-list-init case (I'm not quite convinced
that the wording unambiguously says that, but whatever). Thanks for the
help!
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
'Johannes Schaub' via ISO C++ Standard - Discussion
2017-05-16 08:20:51 UTC
Permalink
BTW, interesting that apparently GCC does consider "operator X&&" to
be better than "operator X". That seems to imply that it thinks that
over.match.ref does not enumerate the latter.

struct X { };
struct Y {
operator X();
operator X&&();
};

X &&x = Y();

GCC does not adhere to the spec, and selects "operator X&&", while
Clang adheres and gives an ambiguity. And that, even though it was GCC
folks that reported
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#656 . I
wonder about the reason that they did not completely implement this
through?
Post by T. C.
OK, I just read through [dcl.init]/17 again. I can't see any distinction
between what it says for the direct-non-list-init case and the
direct-list-init case. After overload resolution, the actual reference
initialization is subject to the same set of rules, so if it works in one it
must work in the other, and the wording at issue is clearly intended to work
at least for the direct-non-list-init case (I'm not quite convinced that the
wording unambiguously says that, but whatever). Thanks for the help!
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...