This question is inspired by comments here.
Consider the following code snippet:
struct X {}; // no virtual members
struct Y : X {}; // may or may not have virtual members, doesn't matter
Y* func(X* x) { return dynamic_cast<Y*>(x); }
Several people suggested that their compiler would reject the body of func
.
However, it appears to me that whether this is defined by the Standard depends on the run-time value of x
. From section 5.2.7 ([expr.dynamic.cast]
):
The result of the expression
dynamic_cast<T>(v)
is the result of converting the expressionv
to typeT
.T
shall be a pointer or reference to a complete class type, or "pointer to cvvoid
." Thedynamic_cast
operator shall not cast away constness.If
T
is a pointer type,v
shall be a prvalue of a pointer to complete class type, and the result is a prvalue of typeT
. IfT
is an lvalue reference type,v
shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to byT
. IfT
is an rvalue reference type,v
shall be an expression having a complete class type, and the result is an xvalue of the type referred to byT
.If the type of
v
is the same asT
, or it is the same asT
except that the class object type inT
is more cv-qualified than the class object type inv
, the result isv
(converted if necessary).If the value of
v
is a null pointer value in the pointer case, the result is the null pointer value of typeT
.If T is "pointer to cv1
B
" andv
has type 'pointer to cv2D
" such thatB
is a base class ofD
, the result is a pointer to the uniqueB
subobject of theD
object pointed to byv
. Similarly, if T is "reference to cv1B
" andv
has type cv2D
such thatB
is a base class ofD
, the result is the uniqueB
subobject of theD
object referred to byv
. The result is an lvalue ifT
is an lvalue reference, or an xvalue ifT
is an rvalue reference. In both the pointer and reference cases, the program is ill-formed if cv2 has greater cv-qualification than cv1 or ifB
is an inaccessible or ambiguous base class ofD
.Otherwise,
v
shall be a pointer to or an lvalue of a polymorphic type.If
T
is "pointer to cvvoid
," then the result is a pointer to the most derived object pointed to byv
. Otherwise, a run-time check is applied to see if the object pointed or referred to byv
can be converted to the type pointed or referred to byT
.) The most derived object pointed or referred to byv
can contain otherB
objects as base classes, but these are ignored.If
C
is the class type to whichT
points or refers, the run-time check logically executes as follows:
If, in the most derived object pointed (referred) to by
v
,v
points (refers) to apublic
base class subobject of aC
object, and if only one object of typeC
is derived from the subobject pointed (referred) to byv
the result points (refers) to thatC
object.Otherwise, if
v
points (refers) to apublic
base class subobject of the most derived object, and the type of the most derived object has a base class, of typeC
, that is unambiguous andpublic
, the result points (refers) to theC
subobject of the most derived object.Otherwise, the run-time check fails.
The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws
std::bad_cast
.
The way I read this, the requirement of a polymorphic type only applies if none of the above conditions are met, and one of those conditions depends on the runtime value.
Of course, in a few cases the compiler can positively determine that the input cannot properly be NULL (for example, when it is the this
pointer), but I still think the compiler cannot reject the code unless it can determine that the statement will be reached (normally a run-time question).
A warning diagnostic is of course valuable here, but is it Standard-compliant for the compiler to reject this code with an error?
A very good point.
Note that in C++03 the wording of 5.2.7/3 and 5.2.7/4 is as follows
3 If the type of v is the same as the required result type (which, for convenience, will be called R in this description), or it is the same as R except that the class object type in R is more cv-qualified than the class object type in v, the result is v (converted if necessary).
4 If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type R.
The reference to type R
introduced in 5.2.7/3 seems to imply that 5.2.7/4 is intended to be a sub-clause of 5.2.7/3. In other words, it appears that 5.2.7/4 is intended to apply only under the conditions described in 5.2.7/3, i.e. when types are the same.
However, the wording in C++11 is different and no longer involves R
, which no longer suggests any special relationship between 5.2.7/3 and 5.2.7/4. I wonder whether it was changed intentionally...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With