Why is it not possible to overload the ternary ope

2019-01-14 07:45发布

问题:

Why is it not possible to overload the ternary operator ' ?: '?

I use the ternary operator often to consolidate if statements, and am curious why the language designers chose to forbid this operator from being overloaded. I looked for an explanation as to why in C++ Operator Overloading but did not find one describing why this isn't possible. The only information the footnote provides is that it cannot be overloaded.

My initial guess is that overloading the operator will almost always violate number one or two of the principles given in the link above. The meaning of the overload will rarely be obvious or clear or it will deviate from its original known semantics.

So my question is more of why is this not possible rather than how, as I know it cannot be done.

回答1:

I think the main reason at the time that it didn't seem worth the effort of inventing a new syntax just for that operator. There is no token ?:, so you'd have to create a number of special grammar rules just for it. (The current grammar rule has operator followed by an operator, which is a single token.)

As we've learned (from experience) to use operator overloading more reasonably, it has become apparent that we really shouldn't have allowed overloading of && and || either, for the reasons other responses have pointed out, and probably not operator comma as well (since the overloaded versions won't have the sequence point which the user expects). So the motivation to support it is even less than it was originally.



回答2:

if you could override the ternary operator, you would have to write something like this:

xxx operator ?: ( bool condition, xxx trueVal, xxx falseVal );

To call your override, the compiler would have to calculate the value of both trueVal and falseVal. That's not how the built-in ternary operator works - it only calculates one of those values, which is why you can write things like:

return p == NULL ? 23 : p->value;

without worrying about indirecting through a NULL pointer.



回答3:

One of the principles of the ternary operator is that the true / false expression are only evaluated based on the truth or falseness of the conditional expression.

cond ? expr1 : expr2

In this example expr1 is only evaluated if cond is true while expr2 is only evaluated if cond is false. Keeping that in mind lets look at what a signature for ternary overloading would look like (using fixed types here instead of a template for simplicity)

Result operator?(const Result& left, const Result& right) { 
  ...
}

This signature simply isn't legal because it violates the exact semantics I described. In order to call this method the language would have to evaluate both expr1 and expr2 hence they are no longer conditionally evaluated. In order to support ternary the operator would either need to

  1. Take a lambda for each value so it could produce them on demand. This would necessarily complicate the calling code though because it would have to take into account lambda call semantics where no lambda was logically present
  2. The ternary operator would need to return a value to denote whether the compiler should use expr1 or expr2

EDIT

Some may argue that the lack of short circuiting in this scenario is fine. The reason being that C++ already allows you to violate short circuiting in operator overloads with || and &&

Result operator&&(const Result& left, const Result& right) { 
  ...
}

Though I still find this behavior baffling even for C++.



回答4:

For the same reason why you really should not (although you can) overload && or || operators - doing so would disable short-circuiting on those operators (evaluating only the necessary part and not everything), which can lead to severe complications.



回答5:

The short and accurate answer is simply "because that's what Bjarne decided."

Although the arguments about which operands should be evaluated and in what sequence give a technically accurate description of what happens, they do little (nothing, really) to explain why this particular operator can't be overloaded.

In particular, the same basic arguments would apply equally well to other operators such as operator && and operator||. In the built-in version of each of these operators, the left operand is evaluated, then if and only if that produces 1 for && or a 0 for ||, the right operand is evaluated. Likewise, the (built in) comma operator evaluates its left operand, then its right operand.

In an overloaded version of any of these operators, both operands are always evaluated (in an unspecified sequence). As such, they're essentially identical to an overloaded ternary operator in this respect. They all lose the same guarantees about what operands are evaluated and in what order.

As to why Bjarne made that decision: I can see a few possibilities. One is that although it's technically an operator, the ternary operator is devoted primarily to flow control, so overloading it would be more like overloading if or while than it is like overloading most other operators.

Another possibility would be that it would be syntactically ugly, requiring the parser to deal with something like operator?:, which requires defining ?: as a token, etc. -- all requiring fairly serious changes to the C grammar. At least in my view, this argument seems pretty weak, as C++ already requires a much more complex parser than C does, and this change would really be much smaller than many other changes that have been made.

Perhaps the strongest argument of all is simply that it didn't seem like it would accomplish much. Since it is devoted primarily to flow control, changing what it does for some types of operands is unlikely to accomplish anything very useful.