1

目标

我(像网络上的许多其他人一样)想Int在数学中使用变量和文字,CGFloat因为到目前为止可读性和易于开发超过了可能的精度损失。当您在整个应用程序中使用手动布局而不是使用情节提要时,这一点最为明显。

因此,以下内容应该可以在没有任何手动CGFloat转换的情况下工作:

let a = CGFloat(1)
let b = Int(2)

let c = a / b  // Cannot invoke / with an arguments list of type (CGFloat, Int)
let d = b / a  // Cannot invoke / with an arguments list of type (Int, CGFloat)
let e = a / 2  // => CGFloat(0.5)
let f = 2 / a  // => CGFloat(2.0)
let g = 2 / b  // => Int(1)
let h = b / 2  // => Int(1)
let i = 2 / 2  // => Int(1)
let j: CGFloat = a / b  // Cannot invoke / with an arguments list of type (CGFloat, Int)
let k: CGFloat = b / a  // Cannot invoke / with an arguments list of type (Int, CGFloat)
let l: CGFloat = a / 2  // => CGFloat(0.5)
let m: CGFloat = 2 / a  // => CGFloat(2.0)
let n: CGFloat = 2 / b  // Cannot invoke / with an arguments list of type (IntegerLiteralConvertible, Int)
let o: CGFloat = b / 2  // Cannot invoke / with an arguments list of type (Int, IntegerLiteralConvertible)
let p: CGFloat = 2 / 2  // => CGFloat(1.0)

方法

由于我们无法将隐式转换添加到 Swift 类型,因此我必须添加适当的运算符,这些运算符采用CGFloatInt.

func / (a: CGFloat, b: Int) -> CGFloat { return a / CGFloat(b) }
func / (a: Int, b: CGFloat) -> CGFloat { return CGFloat(a) / b }

问题

当 Swift 尝试CGFloat从整数文字隐式创建值时,这两个运算符变得模棱两可。它不知道要转换两个操作数中的哪一个(示例 case p)。

let a = CGFloat(1)
let b = Int(2)

let c = a / b  // => CGFloat(0.5)
let d = b / a  // => CGFloat(2.0)
let e = a / 2  // => CGFloat(0.5)
let f = 2 / a  // => CGFloat(2.0)
let g = 2 / b  // => Int(1)
let h = b / 2  // => Int(1)
let i = 2 / 2  // => Int(1)
let j: CGFloat = a / b  // => CGFloat(0.5)
let k: CGFloat = b / a  // => CGFloat(2.0)
let l: CGFloat = a / 2  // => CGFloat(0.5)
let m: CGFloat = 2 / a  // => CGFloat(2.0)
let n: CGFloat = 2 / b  // => CGFloat(1.0)
let o: CGFloat = b / 2  // => CGFloat(1.0)
let p: CGFloat = 2 / 2  // Ambiguous use of operator /

问题

有没有办法以一种没有歧义并且所有测试用例都成功的方式声明运算符?

4

1 回答 1

0

对于初学者,tl; dr:否


问题是我们要求编译器隐式地做太多事情。

我将使用常规函数来回答这个问题,因为我希望清楚这与运算符无关。


因此,我们需要以下 4 个函数:

func foo(a: Int, b: Int) -> Int {
    return 1
}

func foo(a: Int, b: Float) -> Float {
    return 2.0
}

func foo(a: Float, b: Int) -> Float {
    return 3.0
}

func foo(a: Float, b: Float) -> Float {
    return 4.0
}

现在我们处于同样的情况。我将忽略所有有效的方法并专注于这两种情况:

let bar1 = foo(1,2)
let bar2: Float = foo(1,2)

在第一个场景中,我们要求 Swift 隐式地​​确定一件事:应该bar1是什么类型?我们传递给的两个参数foo1类型IntegerLiteralConvertible,和2,它又是类型IntegerLiteralConvertible

因为只有一个覆盖foo需要两个Int参数,所以 Swift 能够确定bar1应该是什么类型foo(Int,Int),这就是覆盖返回的任何类型,即Int.

现在,考虑我们添加以下函数的场景:

func foo(a: Int, b: Int) -> Float {
    return 5.0
}

现在,场景 1 变得模棱两可:

let bar1 = foo(1,2)

我们要求 Swift 在这里隐式地确定两件事:

  1. foo使用哪个覆盖
  2. 用于什么类型bar1

满足场景的方法不止一种。要么bar1是一个Int,我们使用foo(Int,Int)->Int覆盖,要么bar2是一个Float,我们使用foo(Int,Int)->Float覆盖。编译器无法决定。

尽管这样,我们可以使情况不那么模棱两可:

let bar1: Int = foo(1,2)

在这种情况下,编译器知道我们想要foo(Int,Int)->Int覆盖——它是唯一满足该场景的。或者我们可以这样做:

let bar2: Float = foo(1,2)

在这种情况下,编译器知道我们想要foo(Int,Int)->Float覆盖——同样,这是满足场景的唯一方法。


但是让我们回顾一下我的场景 2,这正是您遇到的问题场景:

let bar2: Float = foo(1,2)

没有foo(Int,Int)->Float覆盖(忘记我们添加此覆盖的场景 1 的所有内容)。但是,该类型IntegerLiteralConvertible可以隐式转换为不同类型的数字数据类型(仅字面整数......不是整数变量)。因此,编译器将尝试找到一个foo覆盖,该覆盖接受IntegerLiteralConvertible可以转换为的参数并返回 a Float,我们已明确将其标记为bar2' 类型。

好吧,IntegerLiteralConvertible可以转换为 a Float,因此编译器会找到三个采用正确参数组合的函数:

  • foo(Int,Float) -> Float
  • foo(Float,Int) -> Float
  • foo(Float,Float) -> Float

并且编译器不知道使用哪个。怎么可能?为什么它应该优先将一个或另一个文字整数转换为浮点数?

所以我们得到了歧义问题。

我们可以给编译器另一个覆盖。这与我们在场景 2: 中给出的相同foo(Int,Int) -> Float,现在编译器可以使用以下内容:

let bar2: Float = foo(1,2)

因为在这种情况下,没有隐式转换IntegerLiteralConvertibles,编译器能够找到匹配的函数:foo(Int, Int) -> Float


所以现在你在想:

好吧,让我添加这个foo(Int,Int)->Float,一切都会好起来的!

正确的?

好吧,很抱歉让您失望了,但考虑到以下两个功能:

foo(Int,Int) -> Int
foo(Int,Int) -> Float

我们仍然会遇到歧义问题:

let bar3 = foo(1,2)

有两个覆盖fooIntIntegerLiteralConvertible),因为我们没有指定bar3' 的类型,我们要求编译器找出适当的覆盖并隐式确定bar3' 的类型,这是它无法做到的。

于 2014-11-27T16:34:19.073 回答