I (like many others on the web) would like to use Int
variables and literals in CGFloat
math since readability & ease of development outweigh a possible loss in precision by far. This is most noticeable when you use manual layout throughout an app instead of using the Storyboard.
So the following should work without any manual CGFloat
casts:
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)
Since we cannot add implicit conversions to Swift types I had to add appropriate operators which take CGFloat
and Int
.
func / (a: CGFloat, b: Int) -> CGFloat { return a / CGFloat(b) }
func / (a: Int, b: CGFloat) -> CGFloat { return CGFloat(a) / b }
The two operators become ambiguous when Swift tries to implicitly create CGFloat
values from integer literals. It doesn't know which of the two operands to convert (example 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 /
Is there any way to declare the operators in a way where there is no ambiguous use and all test cases succeed?
For starters, tl;dr: NO .
The problem is that we're asking the compiler to do too much implicitly.
I'm going to use regular functions for this answer, because I want it to be clear that this has nothing to do with operators.
So, we need the following 4 functions:
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
}
And now we're in the same scenario. I'm going to ignore all of the ones that work and focus on these two scenarios:
let bar1 = foo(1,2)
let bar2: Float = foo(1,2)
In the first scenario, we're asking Swift to implicitly determine just one thing: What type should bar1
be? The two arguments we pass to foo
are 1
, which is of type IntegerLiteralConvertible
, and 2
, which is again of the type IntegerLiteralConvertible
.
Because there is only one override for foo
which takes two Int
arguments, Swift is able to figure out what type bar1
should be, and that's whatever type the foo(Int,Int)
override returns, which is Int
.
Now, consider the scenario in which we add the following function:
func foo(a: Int, b: Int) -> Float {
return 5.0
}
Now, scenario 1 becomes ambiguous:
let bar1 = foo(1,2)
We're asking Swift to determine TWO things implicitly here:
foo
to use bar1
There is more than one way to satisfy the scenario. Either bar1
is an Int
and we use the foo(Int,Int)->Int
override, or bar2
is a Float
, and we use the foo(Int,Int)->Float
override. The compiler can't decide.
We can make the situation less ambiguous though as such:
let bar1: Int = foo(1,2)
In which case the compiler knows we want the foo(Int,Int)->Int
override--it's the only one that satisfies the scenario. Or we can do:
let bar2: Float = foo(1,2)
In which case the compiler knows we want the foo(Int,Int)->Float
override--again, the only way to satisfy the scenario.
But let's take a look back at my scenario 2, which is exactly the problem scenario you have:
let bar2: Float = foo(1,2)
There's not a foo(Int,Int)->Float
override (forget everything about scenario 1 in which we added this override). However, the type IntegerLiteralConvertible
can be implicitly cast to different types of numeric data types (only literal integers... not integer variables). So the compiler will try to find a foo
override that takes arguments that IntegerLiteralConvertible
can be cast to and returns a Float
, which we've explicitly marked as bar2
's type.
Well, IntegerLiteralConvertible
can be cast as a Float
, so the compiler finds three functions that take some combination of the right argument:
foo(Int,Float) -> Float
foo(Float,Int) -> Float
foo(Float,Float) -> Float
And the compiler doesn't know which to use. How can it? Why should it prioritize casting one or the other of your literals integers to a float?
So we get the ambiguity problem.
We can give the compiler another override. It's the same one we gave it in scenario 2: foo(Int,Int) -> Float
, and now the compiler is okay with the following:
let bar2: Float = foo(1,2)
Because in this case, without implicitly casting the IntegerLiteralConvertibles
, the compiler was able to find a function that matched: foo(Int, Int) -> Float
.
So now you're thinking:
All right, let me just add this
foo(Int,Int)->Float
and everything will work!
Right?
Well, sorry to disappoint, but given the following two functions:
foo(Int,Int) -> Int
foo(Int,Int) -> Float
We will still run into ambiguity problems:
let bar3 = foo(1,2)
There are two overrides for foo
that take an Int
(or IntegerLiteralConvertible
), and because we haven't specified bar3
's type and we're asking the compiler both figure out the appropriate override and implicitly determine bar3
's type, which it cannot do.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.