使用具有“函数样式”语法('(binaryOp)(lhs,rhs)'和'(unaryOp)(操作数)'的运算符时,运算符重载决策不一致

时间:2016-07-18 20:00:05

标签: swift operator-overloading swift3

问题(/ TL; DR)

在下面的详细信息中,我们看到使用函数样式((unaryOperator)(operand)(binaryOperator)(lhsOperand, rhsOperand))语法来调用操作符函数似乎会覆盖类型检查器的正常重载决策规则;重载只是选择它可以找到的第一个(按照定义的顺序)重载。

  • 这是一个错误吗?或者这是一种特殊的,但预期的函数式操作符((unaryOperator)(operand)(binaryOperator)(lhsOperand, rhsOperand))语法的行为?

背景

我刚刚阅读了Matt Gallagher关于Swift类型检查器当前问题的优秀文章

本文中提出的一个问题是,由于存在非通用替代方案,因此在运算符的重载决策中不考虑通用重载这一事实可能导致的问题。 Gallagher通过以下示例显示了一个这样的“问题”

/* note: Swift 3 */
prefix operator %% {}
prefix func %%(_ x: Float) -> Float { return x }
prefix func %%<I: Integer>(_ x: I) -> I { return x }

let y = %%1          // integer literal
print(y.dynamicType) // "Float"

如果我们在上面的两个%%运算符定义上切换位置,我们自然会看到相同的结果,即

/* ... same result as above */
prefix operator %% {}
prefix func %%<I: Integer>(_ x: I) -> I { return x }
prefix func %%(_ x: Float) -> Float { return x }

另请注意,上述严格非泛型的重载优先级优先级不适用于函数,如Gallagher所示,以下示例

/* resolves x to "Int", no matter the order 
   of these two function definitions */
func f(_ x: Float) -> Float { return x }
func f<I: Integer>(_ x: I) -> I { return x }

let x = f(1)
print(x.dynamicType) // "Int"

关于问题的进一步细节

现在谈到了这个特点。我们可以分别使用(binaryOperator)(lhsOperand, rhsOperand)(unaryOperator)(operand)语法在函数样式中调用二元和一元操作。 E.g。

let a = (+)(1, 2) // 3
let b = (-)(a)    // -3

现在,如果我们将此语法应用于上面的%%运算符示例,则上述重载决策规则似乎不再适用(无论我们是将其视为函数调用还是操作员调​​用) ),因为上例中y的类型似乎完全取决于%%定义出现的顺序。

prefix operator %% {}
prefix func %%(_ x: Float) -> Float { return x }
prefix func %%<I: Integer>(_ x: I) -> I { return x }

let y = (%%)(1)      
print(y.dynamicType) // "Float"

/* swap the order of the definitions ... */
prefix operator %% {}
prefix func %%<I: Integer>(_ x: I) -> I { return x }
prefix func %%(_ x: Float) -> Float { return x }    

let y = (%%)(1)      
print(y.dynamicType) // "Int"

我们甚至可能对于某些特殊条件 ,引入额外的重载(预期)会导致正常%%1情况的模糊性,其中函数样式(%%)(1)案例编译没有歧义,选择它可以找到的第一个定义。

prefix operator %% {}
prefix func %%(_ x: Double) -> Double { return x }  
prefix func %%<I: Integer>(_ x: I) -> I { return x }
prefix func %%(_ x: Float) -> Float { return x }    

// picks first %% definition it can find
let y = (%%)(1)   
print(y.dynamicType) // "Double"

// ambiguity error as expected
//let z = %%(1) // error: ambiguous use of operator '%%'

[†]:这最后一个例子的非错误行为实际上是不可靠的:引入第二个泛型重载,如果首先定义非泛型重载,则片段仅运行且没有错误,而且仅当非泛型重载定义遵循非泛型重载时。然而,我不会进一步讨论这种特殊情况下的疯狂,但它似乎有些“peek”行为:接受第一个定义的非泛型重载,因为下一个定义的窥视显示了一个通用的之一。

(在IBM Swift Sandbox中测试Swift 3.0。在XCode 7.3操场和项目中测试的Swift 2.2改编版本)

0 个答案:

没有答案