Created on1998-12-10.00:00:00 last changed277 months ago
Proposed resolution (10/00):
Change 12.2.4.2 [over.best.ics] paragraphs 3 and 4 from
Except in the context of an initialization by user-definedconversion (12.2.2.5 [over.match.copy],12.2.2.6 [over.match.conv]), a well-formed implicit conversionsequence is one of the following forms:
- astandard conversion sequence(12.2.4.2.2 [over.ics.scs]),
- auser-defined conversion sequence(12.2.4.2.3 [over.ics.user]), or
- anellipsis conversion sequence(12.2.4.2.4 [over.ics.ellipsis])
In the context of an initialization by user-defined conversion (i.e.,when considering the argument of a user-defined conversion function;see 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv]),only standard conversion sequences andellipsis conversion sequences are allowed.
to
A well-formed implicit conversionsequence is one of the following forms:
- astandard conversion sequence(12.2.4.2.2 [over.ics.scs]),
- auser-defined conversion sequence(12.2.4.2.3 [over.ics.user]), or
- anellipsis conversion sequence(12.2.4.2.4 [over.ics.ellipsis])
However, when considering the argument of a user-defined conversionfunction that is a candidate by 12.2.2.4 [over.match.ctor]when invoked for the copyingof the temporary in the second step of a class copy-initialization, orby 12.2.2.5 [over.match.copy], 12.2.2.6 [over.match.conv],or 12.2.2.7 [over.match.ref] in all cases, only standardconversion sequences and ellipsis conversion sequences areallowed.
By the letter of the standard, the conversions required tomakeauto_ptr work should be accepted.
However, there's good reason to wonder if there isn't a bug in the standardhere. Here's the issue: line 16 in the example below comes down to
copy-initialize anauto_ptr<Base> fromanauto_ptr<Derived> rvalueTo do that, we first look to see whether we can convertanauto_ptr<Derived>to anauto_ptr<Base>, by enumerating theconstructors ofauto_ptr<Base>and the conversion functions ofauto_ptr<Derived>.There's a single possible way to do the conversion, namely the conversionfunction
auto_ptr<Derived>::operator auto_ptr<Base>()(generated from the template). (The constructorauto_ptr<Base>(auto_ptr_ref<Base>)doesn't work because it requires a user-defined conversion on theargument.)
So far, so good. Now, we do the copy step:
direct-initialize anauto_ptr<Base>from anauto_ptr<Base>rvalueThis, as we've gone to great lengths to set up, is done by callingthe conversion function
auto_ptr<Base>::operator auto_ptr_ref<Base>()(generated from the template), and then the constructor
auto_ptr<Base>(auto_ptr_ref<Base>)(generated from the template).
The problem with this interpretation is that it violates the long-standingcommon-law rule that only a single user-defined conversion will be calledto do an implicit conversion. I find that pretty disturbing.(In fact, the full operation involves two conversion functions and twoconstructors, but "copy" constructors are generally considered not to beconversions.)
The direct-initialization second step of a copy-initialization was intendedto be a simple copy — you've made a temporary, and now you use a copyconstructor to copy it. Because it is defined in terms of directinitialization, however, it can exploit the loophole that auto_ptris based on.
To switch to personal opinion for a second, I think it's bad enoughthat auto_ptr has to exploit a really arcane loophole of overload resolution,but in this case it seems like it's exploiting a loophole on a loophole.
struct Base { // 2 static void sink(auto_ptr<Base>); // 3 }; // 4 struct Derived : Base { // 5 static void sink(auto_ptr<Derived>); // 6 }; // 7 auto_ptr<Derived> source() { // 8 auto_ptr<Derived> p(source()); // 9 auto_ptr<Derived> pp(p); // 10 Derived::sink(source()); // 11 p = pp; // 12 p = source(); // 13 auto_ptr<Base> q(source()); // 14 auto_ptr<Base> qp(p); // 15 Base::sink(source()); // 16 q = pp; // 17 q = source(); // 18 return p; // 19 return source(); }Derek Inglis:It seems clear to me that the result of this direct initilizationmust be the second standard conversion sequence in a user definedconversion sequence. Otherwise the resulting conversion sequence isnot an implicit conversion sequence. By the letter of the standard,the sequence of conversions making up a copy-initialization must be animplicit conversion sequence.
Paragraph 3 of 7.3 [conv]:
An expressione can beimplicitly converted to a typeT if and only if the declaration "T t=e;" iswell-formed, for some invented temporary variablet(9.5 [dcl.init]).
Paragraph 1 of 12.2.4.2 [over.best.ics]:
Animplicit conversion sequence is a sequence of conversionsused to convert an argument in a function call to the type of thecorresponding parameter of the function being called. The sequence ofconversions is an implicit conversion as defined in7.3 [conv], which means it is governed by the rules forinitialization of an object or reference by a single expression(9.5 [dcl.init], 9.5.4 [dcl.init.ref]).Sentence 1 of paragraph 12 of 9.5 [dcl.init]:
The initialization that occurs in argument passing ...is calledcopy-initialization and is equivalent to the formT x = a;
For me, these sentences imply that all sequences of conversionspermitted on a function argument must be valid implicit conversionsequences.
The 'loophole' can be closed by adding a sentence (or note) to thesection describing the 'direct initialization second step of a copyinitialization' stating that the copy initialization is ill-formed ifthe conversion sequence resulting from the direct initialization isnot a standard conversion sequence.
(See alsoissue 177 and paperJ16/00-0009 = WG21 N1232.)
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2003-04-25 00:00:00 | admin | set | status: dr -> tc1 |
| 2000-11-18 00:00:00 | admin | set | status: ready -> dr |
| 2000-05-21 00:00:00 | admin | set | messages: +msg317 |
| 2000-05-21 00:00:00 | admin | set | status: open -> ready |
| 1998-12-10 00:00:00 | admin | create | |