Why would I need conversion?

In this code:

template<class T>
struct Side
{
};

template<class T>
struct LeftSide : public Side<T>
{
};
template<class T>
struct RightSide : public Side<T>
{
};

Side<int>* f(int left, int right)
{
    return left < right ? new LeftSide<int> : new RightSide<int>;//<---Here I'm returning either left or right side
}

int _tmain(int argc, _TCHAR* argv[])
{
    return 0;
}

I'm getting an error:
_Error 1 error C2446: ':' : no conversion from 'RightSide *' to 'LeftSide *'_

I've thought (wrongly as I see) that I can assign pointer from derived to base without any problems. So where is the problem?

Upvotes: 8

Views: 2585

Answers (4)

John Dibling
John Dibling

Reputation: 101456

The problem is not with the conversion from either LeftSide or RightSide to Side<T>. As you originally thought, this conversion would be fine.

Rather, the problem is with this expression:

left < right ? new LeftSide<int> : new RightSide<int>

Let's break this down a little. The ternary operator (properly referred to in the Standard as the 'comparison operator') looks like this:

bool_val ? lhs_expression : rhs_expression

Keep in mind that this whole construct is itself an expression. Meaning it returns a value, which has to have a type, obv. The type of the whole expression is deduced from the types of lhs_expression and rhs_expression. In this case, you have a LeftSide and a RightSide. So, here's your problem.

LeftSide and RightSide are not directly related to each other other than having a common base class, and there's no conversion available between them. (You'd have to write one.) So there's no single datatype that the bool_val ? lhs_expression : rhs_expression can have. You might think, "well, silly compiler, why not just figure out the common base class and use that?" This is, indeed, a bit of a pain. Leaving the argument of it being Right or Wrong aside, it just doesn't work that way.

You have two options.

One, use a simpler construct:

if( left < right )
  return new LeftSide<int>;
else
  return new RightSide<int>;

Two, if you really really want to use the ternary operator (which is the case sometimes), you need to spoon-feed the compiler it's datatypes:

Side<int>* f(int left, int right)
{
    return left < right ? static_cast<Side<int>*>(new LeftSide<int>) : static_cast<Side<int>*>(new RightSide<int>);// now you're good
}

Upvotes: 15

pm100
pm100

Reputation: 50110

I think that the ? : operator requires that the 2 choices be the same type; not that they can be converted to the same type

FYI gcc fails the same

 error: conditional expression between distinct pointer types ‘LeftSide<int>*’ and ‘RightSide<int>*’ lacks a cast

casting both to Side(int)* works (but you probably knew that already)

Upvotes: 4

SoapBox
SoapBox

Reputation: 20609

The two should be the same type, or one should be convertible to the other.

return left < right ? (Side<int>*)new LeftSide<int> : (Side<int>*)new RightSide<int>;

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283614

You want both branches to return a Side<int>*, but the compiler doesn't know that, the type Side<int> doesn't appear anywhere in that expression.

Since I don't like to use a cast when an implicit conversion exists, I'd write this as:

 if (left < right) return new LeftSide<int>;
 return new RightSide<int>;

But if you want to use a ternary operator,

 Side<int>* i_want_this_type;
 return (left < right) ? new LeftSide<int> : (i_want_this_type = new RightSide<int>);

Now the right hand branch is type Side<int>*, the left hand is convertible to that type, everything is ok (and the compiler optimizes out the extra variable).

Upvotes: 2

Related Questions