Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting pointers and the ternary ?: operator. Have I reinvented the wheel?

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

like image 477
Aaron McDaid Avatar asked May 30 '11 17:05

Aaron McDaid


People also ask

What does the ternary operator do?

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.

What is conditional or ternary operator give syntax and example?

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.

Why conditional operator is known as ternary operator explain with an example?

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.


3 Answers

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.

like image 124
Konrad Rudolph Avatar answered Oct 24 '22 14:10

Konrad Rudolph


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.

like image 35
Lightness Races in Orbit Avatar answered Oct 24 '22 15:10

Lightness Races in Orbit


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.

like image 26
David Rodríguez - dribeas Avatar answered Oct 24 '22 13:10

David Rodríguez - dribeas