Reputation: 24675
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
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
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
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
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