class A{};
A f() { return A(); }
A&& a = static_cast<A&&>(f());
An expression e can be explicitly converted to a type T if there is an implicit conversion sequence ([over.best.ics]) from e to T, or if overload resolution for a direct-initialization ([dcl.init]) of an object or reference of type T from e would find at least one viable function ([over.match.viable]). If T is a reference type, the effect is the same as performing the declaration and initialization
T t(e);
for some invented temporary variable t ([dcl.init]) and then using the temporary variable as the result of the conversion. Otherwise, the result object is direct-initialized from e. [ Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. — end note ]
Now look at the changes proposed in P0135R1 for the two paragraphs:
Change in 5.2.9 [expr.static.cast] paragraph 3:
AnDrafting note: the deleted cases would also be handled by the immediately-following paragraph; we only need the special case above to convert lvalues to xvalues.glvalue, class prvalue, or array prvalueof type "cv1 T1" can be cast to type "rvalue reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1" (8.5.3). If the value is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversion (4.1) is applied to the bit-field and the resulting prvalue is used as the expression of the static_cast for the remainder of this section. If T2 is an inaccessible (Clause 11) or ambiguous (10.2) base class of T1, a program that necessitates such a cast is ill-formed.
Change in 5.2.9 [expr.static.cast] paragraph 4:
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5).The effect of such an explicit conversionIf T is a reference type, the effect is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. Otherwise, the result object is direct-initialized from e.The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.
Consider the following snippet. It compiles in C++14 and AFAIK it should continue to compile after the changes introduced by P0135R1.
class A{};
A f() { return A(); }
A&& a = static_cast<A&&>(f());Note that [expr.static.cast]/3 used to allow the conversion above, but not anymore after the changes introduced by P0135R1, as the expression f() is not an lvalue.[expr.static.cast]/3:An lvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1” ([dcl.init.ref]). If the value is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversion ([conv.lval]) is applied to the bit-field and the resulting prvalue is used as the expression of the static_cast for the remainder of this section. If T2 is an inaccessible (Clause [class.access]) or ambiguous ([class.member.lookup]) base class of T1, a program that necessitates such a cast is ill-formed.Then I looked at [expr.static.cast]/4. It just doesn't say anything about static_cast !!
[expr.static.cast]/4:An expression e can be explicitly converted to a type T if there is an implicit conversion sequence ([over.best.ics]) from e to T, or if overload resolution for a direct-initialization ([dcl.init]) of an object or reference of type T from e would find at least one viable function ([over.match.viable]). If T is a reference type, the effect is the same as performing the declaration and initialization
T t(e);for some invented temporary variable t ([dcl.init]) and then using the temporary variable as the result of the conversion. Otherwise, the result object is direct-initialized from e. [ Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. — end note ]
Now look at the changes proposed in P0135R1 for the two paragraphs:
Change in 5.2.9 [expr.static.cast] paragraph 3:
AnDrafting note: the deleted cases would also be handled by the immediately-following paragraph; we only need the special case above to convert lvalues to xvalues.glvalue, class prvalue, or array prvalueof type "cv1 T1" can be cast to type "rvalue reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1" (8.5.3). If the value is not a bit-field, the result refers to the object or the specified base class subobject thereof; otherwise, the lvalue-to-rvalue conversion (4.1) is applied to the bit-field and the resulting prvalue is used as the expression of the static_cast for the remainder of this section. If T2 is an inaccessible (Clause 11) or ambiguous (10.2) base class of T1, a program that necessitates such a cast is ill-formed.Change in 5.2.9 [expr.static.cast] paragraph 4:
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5).The effect of such an explicit conversionIf T is a reference type, the effect is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. Otherwise, the result object is direct-initialized from e.The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.Somehow the changes stated above in paragraph 4 were not considered in the current version of C++1z. And more, the current wording in paragraph 4 doesn't seem to allow the alluded snippet to compile.
--
---
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+unsubscribe@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
That's implied; all of this subclause is about static_cast.
On Sunday, September 18, 2016 at 1:59:07 PM UTC-3, Richard Smith wrote:That's implied; all of this subclause is about static_cast.But the statement in P0135R1 is much clearer than the one in C++1z:
Change in 5.2.9 [expr.static.cast] paragraph 4:An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5).The effect of such an explicit conversionIf T is a reference type, the effect is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. Otherwise, the result object is direct-initialized from e.The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.
--
On Sun, Sep 18, 2016 at 10:53 AM, Belloc <jabe...@gmail.com> wrote:
On Sunday, September 18, 2016 at 1:59:07 PM UTC-3, Richard Smith wrote:That's implied; all of this subclause is about static_cast.But the statement in P0135R1 is much clearer than the one in C++1z:The mention of static_cast here was removed by CWG issue 242. It seems clear enough to me from context that the rules given in [expr.static.cast] are rules for static_cast, and I'm not persuaded that clarity would be improved by repeating this in every paragraph.
The meaning of an old-style cast is described in terms of const_cast, static_cast, and reinterpret_cast in 5.4 [expr.cast] paragraph 5. Ignoring const_cast for the moment, it basically says that if the conversion performed by a given old-style cast is one of those performed by static_cast, the conversion is interpreted as if it were a static_cast; otherwise, it's interpreted as if it were a reinterpret_cast, if possible. The following example is given in illustration:
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
A *foo( D *p ) {
return (A*)( p ); // ill-formed static_cast interpretation
}
The obvious intent here is that a derived-to-base pointer conversion is one of the conversions that can be performed using static_cast, so (A*)(p) is equivalent to static_cast<A*>(p), which is ill-formed because of the ambiguity.
Unfortunately, the description of static_cast in 5.2.9 [expr.static.cast] does NOT support this interpretation. The problem is in the way 5.2.9 [expr.static.cast] lists the kinds of casts that can be performed using static_cast. Rather than saying something like "All standard conversions can be performed using static_cast," it says
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration "T t(e);" is well-formed, for some invented temporary variable t.
Given the declarations above, the hypothetical declaration
A* t(p);
(emphasis is mine):A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.Well, both [expre.static.cast]/4 and [conv.ptr]/3 say that the conversion is ill-formed in case of ambiguity. The ambiguity was a problem with the previous wording in [expr.static.cast]/4 and, as far as I can tell, the problem didn't disappear with the new wording for the paragraph.
--
You're looking in the wrong place. The new wording says to look for an "implicit conversion sequence", and directs you to 13.3.3.1 where that term is defined. [conv.ptr] is talking about an "implicit conversion", which is a different kind of thing. See 13.3.3.1/2 for more about implicit conversion sequences:
You're looking in the wrong place. The new wording says to look for an "implicit conversion sequence", and directs you to 13.3.3.1 where that term is defined. [conv.ptr] is talking about an "implicit conversion", which is a different kind of thing. See 13.3.3.1/2 for more about implicit conversion sequences:
Change 5.2.9 [expr.static.cast] paragraph 2 as follows:
An lvalue of type “cv1 B”, where B is a class type, can be cast to type “reference to cv2 D”, where D is a class derived (Clause 10 [class.derived]) from B, if a valid standard conversion from “pointer to D” to “pointer toB” exists (4.10 [conv.ptr]), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and. If B is neither a virtual base class of D nor or a base class of a virtual base class of D, or if no valid standard conversion from “pointer to D” to “pointer to B” exists (4.10 [conv.ptr]), the program is ill-formed. The result has type “cv2 D”. An xvalue of type “cv1 B” may can be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B”. If the object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the behavior is undefined. [Example:...
Change 5.2.9 [expr.static.cast] paragraph 4 as follows:
An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5 [dcl.init]) there is an implicit conversion sequence (13.3.3.1 [over.best.ics]) from e to T, or if overload resolution for a direct-initialization (8.5 [dcl.init]) of an object or reference of type T from e would find at least one viable function (13.3.2 [over.match.viable]). The effect of such an explicit conversion is the same as performing the declaration and initialization
T t(e);
for some invented temporary variable t (8.5 [dcl.init]) and then using the temporary variable as the result of the conversion. [Note: The conversion is ill-formed when attempting to convert an expression of class type to an inaccessible or ambiguous base class. —end note] The expression e is used as a glvalue if and only if the initialization uses it as a glvalue.