The last line of this code fails to compile with castingAndTernary.cpp:15: error: conditional expression between distinct pointer types ‘D1*’ and ‘D2*’ lacks a cast
A really smart compiler could have no difficulty because both can be safely casted to B*
(the base class). I'm reluctant to use static_cast and dynamic_cast and so on - I'm worried that I'll mix up the classes someday and get undefined behaviour. That's why I created the up_cast template. This template does the bare minimum in allowed conversion. Is there a simpler way? There are other workarounds, but I can't help but think that there's something even simpler and safer that I could use?
struct B{ };
struct D1 : public B{ };
struct D2 : public B{ };
template<typename T,typename V>
T up_cast(V x) {
return x;
}
int main() {
bool boolean_expression = true;
B * b;
b = new D1;
b = new D2;
b = boolean_expression ? up_cast<B*>(new D1) : up_cast<B*>(new D2);
b = boolean_expression ? new D1 : new D2;
}
g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3
Update changed name from implicit_cast
to up_cast
as per @Konrad's answer
The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark ( ? ), then an expression to execute if the condition is truthy followed by a colon ( : ), and finally the expression to execute if the condition is falsy.
Example: C Ternary Operator Here, age >= 18 - test condition that checks if input value is greater or equal to 18. printf("You can vote") - expression1 that is executed if condition is true. printf("You cannot vote") - expression2 that is executed if condition is false.
The conditional operator is also known as a ternary operator. The conditional statements are the decision-making statements which depends upon the output of the expression. It is represented by two symbols, i.e., '?' and ':'. As conditional operator works on three operands, so it is also known as the ternary operator.
A really smart compiler could have no difficulty because both can be safely casted to
B*
Irrelevant. The standard mandates this behaviour. A really smart compiler behaves as observed.
The use of your custom cast is actually fine (and your reluctance for using an explicit cast is well-placed). However, I’d use a different name: upcast
– since that’s happening here: a cast upwards in the inheritance hierarchy.
The [ternary] conditional operator requires its second and third operands to have the same type.
b = boolean_expression ? new D1 : new D2;
You have the distinct types D1*
and D2*
. As the error message indicates, you have to ensure the correct type with an explicit conversion (i.e. a cast):
b = boolean_expression ? static_cast<B*>(new D1) : static_cast<B*>(new D2);
The standard says that compilers must require this (instead of just doing an implicit conversion), so that's what your compiler requires.
I was not going to answer, but after posting the comment I thought, what the... it is an approach as any other:
int main() {
bool condition = true;
D1 d1;
D2 d2;
B * p = condition ? &d1 : (true? &d2 : p );
}
Basically abuse the ternary operator to extract the appropriate type. When the compiler processes the ternary operator it tries to determine whether the two operands can be implicitly converted to a common type 1, and if so, it uses that common type as the type of the expression.
In the code above, the inner ternary operator: true? &d2 : p
will try to match the type of the expression &d2
with the type of p
, it will find that there is a simple upcast that it can perform and will set the return type for that subexpression to B*
. Note that because the condition is true
, it will always yield &d2
, even if it uses the third argument to determine the type.
The same operation is performed with the enclosing expression, where now the second argument is &d1
(type D1*
) and the type of the third argument is B*
. Again, the conversion is trivial by upcasting D1*
, and the type of the whole expression is B*
.
Because all of the conversions are performed implicitly by the compiler, if you change the types of the pointers, and break the invariant that they can be implicitly converted, the compiler will tell you, solving the issue of throwing a static_cast
in the middle of the ternary operator.
1 The standard dictates a set of different conversions, depending on the types of the arguments. In the particular case of the two arguments being pointers (as is the case here) the conversions that are allowed are pointer conversions and qualification conversions.
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