如何在Swift中正确支持CGFloat数学中的Int值?

时间:2014-11-27 15:08:48

标签: swift casting implicit-conversion implicit-typing

目标

我(与网络上的许多其他人一样)希望在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 /

问题

有没有办法以不模糊的方式声明运算符并且所有测试用例都成功?

1 个答案:

答案 0 :(得分:0)

首先, tl; dr:NO


问题在于我们要求编译器隐式地做太多。

我将使用常规函数来获得这个答案,因为我希望它清楚这与运算符无关。


因此,我们需要以下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应该是什么类型?我们传递给foo的两个参数是1,其类型为IntegerLiteralConvertible,而2则属于IntegerLiteralConvertible类型。

因为foo只有一个覆盖需要两个Int个参数,所以Swift能够找出bar1应该是什么类型,而foo(Int,Int)的类型是Int覆盖返回,即func foo(a: Int, b: Int) -> Float { return 5.0 }

现在,考虑我们添加以下功能的场景:

let bar1 = foo(1,2)

现在,方案1变得含糊不清:

foo

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

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

    满足该方案的方法不止一种。 Intfoo(Int,Int)->Int我们使用bar2覆盖,或Floatfoo(Int,Int)->Float,我们使用let bar1: Int = foo(1,2) 覆盖。编译器无法决定。

    尽管如此,我们可以使情况变得模棱两可:

    foo(Int,Int)->Int

    在这种情况下,编译器知道我们想要let bar2: Float = foo(1,2) 覆盖 - 它是唯一满足该方案的方法。或者我们可以这样做:

    foo(Int,Int)->Float

    在这种情况下,编译器知道我们想要let bar2: Float = foo(1,2) 覆盖 - 再次,这是满足场景的唯一方法。


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

    foo(Int,Int)->Float

    没有IntegerLiteralConvertible覆盖(忘记我们添加此覆盖的方案1的所有内容)。但是,类型foo可以隐式转换为不同类型的数值数据类型(只有文字整数...而不是整数变量)。因此,编译器将尝试查找IntegerLiteralConvertible覆盖,该覆盖采用Float可以强制转换为bar2的参数并返回IntegerLiteralConvertible,我们已明确标记为Float类型。

    好吧,foo(Int,Float) -> Float可以强制转换为foo(Float,Int) -> Float,因此编译器会找到三个函数,这些函数将右参数组合在一起:

    • foo(Float,Float) -> Float
    • foo(Int,Int) -> Float
    • let bar2: Float = foo(1,2)

    编译器不知道使用哪个。怎么样?为什么要优先将一个或另一个文字整数强制转换为浮点数?

    所以我们得到了模棱两可的问题。

    我们可以给编译器另一个覆盖。它与我们在场景2中给出的相同:IntegerLiteralConvertibles,现在编译器可以使用以下内容:

    foo(Int, Int) -> Float

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


    所以现在你在想:

      

    好吧,让我添加一下let bar3 = foo(1,2) ,一切都会有效!

    右?

    嗯,抱歉让人失望,但考虑到以下两个功能:

    foo

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

    Int

    IntegerLiteralConvertible有两个覆盖bar3(或bar3)的覆盖,因为我们没有指定{{1}}的类型,我们要求编译器都会找出适当的覆盖并隐式确定{{1}}的类型,这是不可能的。