Open ranaanoop opened 4 months ago
The note at the end of [over.match.list] clarifies that the ill-formedness only applies if we'll end up with the so-constructed sequence at the very end:
"This restriction only applies if this initialization is part of the final result of overload resolution."
Please post a bug report to gcc.
This is actually a duplicate of CWG2525.
GCC takes the point of view that the explicitness of the constructor doesn't affect the formation of the ICS, which makes the overload resolution ambiguous. Clang and MSVC take the point of view that if the ICS would be ill-formed due to an explicit constructor being used for copy-list-initialization, then the ICS doesn't exist.
@jensmaurer A gcc bug was posted for the same but they've closed that bug saying that gcc is right in rejecting the code.
The note at the end of [over.match.list] clarifies that the ill-formedness only applies if we'll end up with the so-constructed sequence at the very end:
But notes are non-normative? Shouldn't this be normative.
@t3nsor , I can understand those differences in viewpoint for the particular special "ill-formed" rule we have here. Why is that related to CWG2525, where we discuss the wording for forming ICS in general?
@ranaanoop , Ah, so the rejection by gcc is not because of the special "ill-formed" rule, but because of ambiguous overload resolution before we (presumably) hit the ill-formed rule.
CWG2525 is all about the fact that implicit conversion sequences are able to be formed under some situations where the actual copy-initialization would be ill-formed (assuming, of course, that the function with which that ICS is associated doesn't get selected), but the standard doesn't explain exactly which types of ill-formedness we let slip when forming an ICS.
@jensmaurer That seems to be the case looking at the error(of gcc). I also noted that a deleted answer was posted which also implied/said that the "program" is ill-formed instead of saying that only the "initialization" is ill-formed when matching {4.2}
against func(std::vector<A>)
. Since that answer was deleted by their user I am posting that answer below:
GCC is correct:
Per [over.ics.list]/7, in order to determine the conversion rank for
{ 4.2 }
for each of the two candidate functions, overload resolution according to [over.match.best] is done as if initializing a variable of the function parameters type from= { 4.2 }
.According to [over.match.best], for that nested overload resolution, there are two phases in which constructors are considered. First
std::initializer_list
constructors and then all constructors. Although it is copy-list-initialization, all constructors are considered in that second phase, not only non-explicit
ones. If anexplicit
constructor is chosen at the end, then the program is ill-formed.
std::vector<A>
does not have a viablestd::initializer_list
constructor, but it does have a constructor with only non-defaulted parameter beingstd::vector::size_type
. This is an integral type to which4.2
(adouble
) can be implicitly converted. That constructor isexplicit
, but as [over.match.list] says, it is considered regardless.The nested overload resolution therefore succeeds in choosing that constructor for a user-defined conversion sequence. But because it uses an
explicit
constructor in copy-list-initialization, the program is ill-formed per [over.match.list].That this may be unintuitive behavior was subject of a CWG issue that was closed as not-a-defect with a remark that the behavior is intended: CWG 1228
Note the emphasis on the "program" is ill-formed. It is not clear to me why the word "ill-formed" is used/chosen in [over.match.list] instead of saying something like "it is not viable". IMO the word ill-formed looks to be too strong here and it seems to imply that if some construct is ill-formed then the whole program will also be ill-formed. So it should be clarified by say making the note given at the end there(that you pointed out) to be normative or just not using the word ill-formed.
But "ill-formed" is intended (when the conversion is actually chosen), as is the overload resolution ambiguity, as can be seen in CWG1228.
Is there anything left between CWG1228 and CWG2525 that is novel for this issue?
Full name of submitter: Anoop Rana
Reference (section label): [over.match.list]
Link to reflector thread (if any): https://stackoverflow.com/a/77979372/12002570
Issue description:
The following program is rejected by gcc but both clang and msvc accepts. AFAIK it is invalid per the current wording. But I think it should be made valid because overload resolution usually discards the non-viable options instead of making the program ill-formed. The reason is explained in this answer and for completeness here is the same:
Step 1
First let us consider the overload resolution for the call
func({4.2})
to the first overloadfunc(std::vector<double>)
.Note note that
func({ 4.2 })
is copy-initialization:Now we move onto dcl.init.general#16 to see that this will use list initialization:
So from dcl.init.list we also see that this is copy-list-initialization:
Finally we move onto dcl.init.list#3:
This means that overload resolution is done with
std::vector
's for the argument{4.2}
and the best one will be choosen.So we move onto over.match.list:
Note that since an initializer list ctor was found, so overload resolution won't be performed again. This in turn means that the initializer list ctor is the choosen option when matching
func({4.2})
against the first overloadfunc(std::vector<double>)
Step 2
Now we see how
func({4.2})
matches against the second overloadfunc(std::vector<A>)
.In this case, almost all the steps are same(as in the last case) except that this time the initializer list ctor
std::vector(std::initializer_list<A>)
is not viable and so the statement if no viable initializer-list constructor is found, overload resolution is performed again is satisfied and soThis means that this time, the
std::size_t
argument ctor ofstd::vector
will be choosen. But note that this ctor ofstd::vector
isexplicit
and we have:Thus the selection of
size_t
argument ctor ofstd::vector
makes the program ill-formed.