为什么不能重载三目运算符'?:'?
我经常使用三目运算符来整合if语句,很好奇为什么语言设计者选择禁止重载这个运算符。我在C++ Operator Overloading中寻找解释,但没有找到说明为什么不可能的原因。脚注提供的唯一信息是它不能被重载。
我的初步猜测是,重载运算符几乎总会违反上述链接中给出的第一或第二原则。重载的含义很少是明显或清晰的,或者它将偏离其原始已知语义。
所以我的问题更多的是为什么不可能而不是如何,因为我知道它无法完成。
为什么不能重载三目运算符'?:'?
我经常使用三目运算符来整合if语句,很好奇为什么语言设计者选择禁止重载这个运算符。我在C++ Operator Overloading中寻找解释,但没有找到说明为什么不可能的原因。脚注提供的唯一信息是它不能被重载。
我的初步猜测是,重载运算符几乎总会违反上述链接中给出的第一或第二原则。重载的含义很少是明显或清晰的,或者它将偏离其原始已知语义。
所以我的问题更多的是为什么不可能而不是如何,因为我知道它无法完成。
如果您可以覆盖三元运算符,那么您需要编写类似于以下内容:
xxx operator ?: ( bool condition, xxx trueVal, xxx falseVal );
为了调用您的重写函数,编译器需要计算 trueVal
和 falseVal
的值。这不是内置三元运算符的工作方式 - 它只计算其中一个值,这就是为什么您可以编写以下内容的原因:
return p == NULL ? 23 : p->value;
无需担心通过空指针间接访问的问题。
&&
和 ||
,你是可以重载它们的。 - James Kanze&&
、||
和,
运算符,除非程序员包含了某些特殊头文件(或传递了特殊编译器开关)。我认为让一个语言在向后兼容的祭坛上永久地传递过去的错误是不明智的。 - Nik Bougalis||
,你会发现供应商将继续支持它作为扩展,因为他们的客户正在使用它——无论他们是否应该使用它,毕竟这是他们的钱...) - David Rodríguez - dribeas我认为当时没必要为了这个运算符发明一种新的语法,主要原因在于:没有 ?:
的标记,所以你需要为此创建若干特定的语法规则。(现行语法规则是一个操作符后面跟着另一个操作符,这是一个单独的标记。)
随着我们(通过经验)更合理地使用运算符重载,已经变得很明显,我们实际上也不应该允许对 &&
和 ||
进行重载,原因如其他回答所指出的那样,可能也不应该支持逗号运算符(由于重载版本不会有用户预期的序列点)。因此,支持它的动机甚至比最初还要少。
expr : expr '?' expr ':' expr { $$ = new ConditionalOp($1, $3, $5); };
- Daniel Frużyńskicond ? expr1 : expr2
expr1
,而只有当条件为假时才会评估expr2
。请记住这一点,让我们看一下三元运算符重载的签名应该是什么样子的(这里使用固定类型而不是模板是为了简单起见)。Result operator?(const Result& left, const Result& right) {
...
}
expr1
和expr2
,因此它们不再是有条件地评估的。为了支持三元运算符,该运算符需要:
expr1
还是expr2
||
和&&
的运算符重载中违反短路。Result operator&&(const Result& left, const Result& right) {
...
}
之前的回答都集中在短路计算上,这是有点道理的,但我认为这并不是尝试这样做的真正问题。
最接近现有三元操作符的实现(没有短路计算)应该像这样:
template<typename T0, typename T1>
std::variant<T0, T1>&& operator?:(bool predicate, T0&& arg0, T1&& arg1)
{
if(predicate)
return { std::forward<T0&&>(arg0) };
return { std::forward<T1&&>(arg1); }
}
然而,T0可能是无效的。T1也可能是无效的。在这两种情况下都无法构建。
使用variant是必要的,因为T0和T1可能不能互相隐式转换,并且返回类型不能用于函数重载决策,这是C++17库的一个补充。但它仍然不是很实用,因为variant不能隐式转换为其任何可能的类型。
&&
或||
运算符-这样做会禁用这些运算符上的短路(仅评估必要部分而不是所有内容),这可能会导致严重的复杂性。
if-else
语句,你如何以有意义的方式进行更改? - Mats Petersson