Mere moments ago, quoth I:
A common type does not need to represent all values of both types; only enough to determine equivalence. This usually means the "common type" is the smaller type, because a failure to convert the larger type to the smaller type inherently implies non-equivalence.
Applying this to the optional<T> example used previously, the "common type" is T, and any failure to convert optional<T> to T can just return false, and this is perfectly correct. More generally, the "common type" would be the *intersection* between the two types (not the union, as you stated); any failure to convert either argument type to the common type can just return false, because if one value cannot be represented in the other type then they cannot possibly be equivalent. Assuming it doesn't fastpath out, you now have two values of the same common type that can use that type's homogenous operator==. It should not matter which order it compares these in, unless commutability has been violated. (For product types, you might need to repeat this for additional pairs of members, of course, but it can short-circuit as soon as there is a failure.)