为什么不能使三元运算符超载?

时间:2021-04-13 22:30:00

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.

我经常使用三元运算符来合并if语句,并且很好奇为什么语言设计者选择禁止此运算符过载。我找到了解释为什么在C ++运算符重载但未找到描述为什么这是不可能的解释。脚注提供的唯一信息是它不能超载。

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.

所以我的问题更多的是为什么这不可能而不是如何,因为我知道它无法完成。

5 个解决方案

#1


17  

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


16  

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:

要调用覆盖,编译器必须计算trueVal和falseVal的值。这不是内置三元运算符的工作原理 - 它只计算其中一个值,这就是为什么你可以编写如下内容的原因:

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

without worrying about indirecting through a NULL pointer.

而不用担心通过NULL指针间接。

#3


6  

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)

在此示例中,仅当cond为true时才计算expr1,而仅在cond为false时计算expr2。记住这一点让我们看一下三元重载的签名是什么样的(为了简单起见,使用固定类型而不是模板)

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

这个签名根本不合法,因为它违反了我描述的确切语义。为了调用此方法,语言必须同时评估expr1和expr2,因此不再对它们进行条件评估。为了支持三元运营商要么需要

  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. 为每个值取一个lambda,以便按需生成它们。这必然会使调用代码复杂化,因为它必须考虑lambda调用语义,其中没有lambda逻辑上存在

  3. The ternary operator would need to return a value to denote whether the compiler should use expr1 or expr2
  4. 三元运算符需要返回一个值来表示编译器是否应该使用expr1或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 &&

有些人可能认为在这种情况下缺乏短路是可以的。原因是C ++已经允许您使用||违反运算符重载中的短路和&&

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

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

虽然我仍然发现这种行为令人困惑,即使对于C ++也是如此。

#4


1  

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


1  

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

简短而准确的回答只是“因为那是Bjarne决定的。”

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.

特别是,相同的基本参数同样适用于其他运算符,例如operator &&和operator ||。在每个运算符的内置版本中,左操作数被计算,然后当且仅当它为&&生成1或者为||生成0时,将评估右操作数。同样,(内置)逗号运算符计算其左操作数,然后计算其右操作数。

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.

至于Bjarne做出这个决定的原因:我可以看到一些可能性。一个是虽然它在技术上是一个操作员,但三元运算符主要用于流量控制,因此重载它更像是重载,如果或者像重载大多数其他运算符一样。

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.

另一种可能性是它会在语法上难看,要求解析器处理像运算符?:这样的东西,它需要定义?:作为标记等等 - 所有这些都需要对C语法进行相当严重的更改。至少在我看来,这个论点似乎相当弱,因为C ++已经需要比C更复杂的解析器,而且这种改变实际上要比已经做出的许多其他改变小得多。

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.

也许所有人最强烈的论据就是它似乎没有取得多大成就。由于它主要用于流控制,因此改变它对某些类型操作数的作用不太可能完成任何非常有用的操作。

#1


17  

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


16  

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:

要调用覆盖,编译器必须计算trueVal和falseVal的值。这不是内置三元运算符的工作原理 - 它只计算其中一个值,这就是为什么你可以编写如下内容的原因:

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

without worrying about indirecting through a NULL pointer.

而不用担心通过NULL指针间接。

#3


6  

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)

在此示例中,仅当cond为true时才计算expr1,而仅在cond为false时计算expr2。记住这一点让我们看一下三元重载的签名是什么样的(为了简单起见,使用固定类型而不是模板)

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

这个签名根本不合法,因为它违反了我描述的确切语义。为了调用此方法,语言必须同时评估expr1和expr2,因此不再对它们进行条件评估。为了支持三元运营商要么需要

  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. 为每个值取一个lambda,以便按需生成它们。这必然会使调用代码复杂化,因为它必须考虑lambda调用语义,其中没有lambda逻辑上存在

  3. The ternary operator would need to return a value to denote whether the compiler should use expr1 or expr2
  4. 三元运算符需要返回一个值来表示编译器是否应该使用expr1或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 &&

有些人可能认为在这种情况下缺乏短路是可以的。原因是C ++已经允许您使用||违反运算符重载中的短路和&&

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

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

虽然我仍然发现这种行为令人困惑,即使对于C ++也是如此。

#4


1  

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


1  

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

简短而准确的回答只是“因为那是Bjarne决定的。”

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.

特别是,相同的基本参数同样适用于其他运算符,例如operator &&和operator ||。在每个运算符的内置版本中,左操作数被计算,然后当且仅当它为&&生成1或者为||生成0时,将评估右操作数。同样,(内置)逗号运算符计算其左操作数,然后计算其右操作数。

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.

至于Bjarne做出这个决定的原因:我可以看到一些可能性。一个是虽然它在技术上是一个操作员,但三元运算符主要用于流量控制,因此重载它更像是重载,如果或者像重载大多数其他运算符一样。

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.

另一种可能性是它会在语法上难看,要求解析器处理像运算符?:这样的东西,它需要定义?:作为标记等等 - 所有这些都需要对C语法进行相当严重的更改。至少在我看来,这个论点似乎相当弱,因为C ++已经需要比C更复杂的解析器,而且这种改变实际上要比已经做出的许多其他改变小得多。

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.

也许所有人最强烈的论据就是它似乎没有取得多大成就。由于它主要用于流控制,因此改变它对某些类型操作数的作用不太可能完成任何非常有用的操作。