IntegerType:无法使用 'T' 类型的参数调用 'init'

IntegerType: Cannot invoke 'init' with an argument of type 'T'

我有这个函数 returns 范围内的整数:

func randomNumber<T: IntegerType>(min: T, max: T) -> T {
    let n = max - min + 1
    let u = UInt32(n)            // Error: Cannot invoke 'init' with an argument of type 'T'
    let r = arc4random_uniform(u) 
    return r + min
}

我不明白为什么这不起作用,因为 UInt32 最上面的协议是 UnsignedIntegerType,它符合 IntegerType.

我必须将 n 变成 UInt32 因为 arc4random_uniform() 需要一个 UInt32 作为参数

为什么这行不通?

麻烦的是UInt32没有init可以任意取IntegerType。当然,它需要标准库中的每一个定义,但是如果有人实现了一个符合 IntegerTypeUInt128 怎么办?即使您在 let u = UInt32(n.toIntMax()) 中进行了替换,您在尝试将 r 添加到 min 时也会被卡住,因为再一次,没有 + 的实现可以添加a UInt32 到任意 IntegerType。考虑到溢出的可能性,这是有道理的——你知道 arc4random_uniform(u) 永远不会 return UInt32 大于 Int8.max,但是 Swift 不能。您需要比 IntegerType 提供的更丰富的功能来编写此函数的真正通用版本,该版本具有正确的前置条件和 post 条件。

您可以创建一个新的整数协议来接受任何 UInt 并将 UInt64 限制为 UInt32.max 以符合 arc4random_uniform 限制,如下所示:

protocol Integer  {
    init(_ value:Int)
    var integerValue: Int { get }
}

extension Int     : Integer { var integerValue : Int { return self      } }
extension UInt64  : Integer { var integerValue : Int { return Int(self) } }
extension UInt32  : Integer { var integerValue : Int { return Int(self) } }
extension UInt16  : Integer { var integerValue : Int { return Int(self) } }
extension UInt8   : Integer { var integerValue : Int { return Int(self) } }
extension UInt    : Integer { var integerValue : Int { return Int(self) } }

func randomNumber(min: Integer, max: Integer) -> Int {
    if min.integerValue >= max.integerValue             { return 0 }
    if max.integerValue-min.integerValue+1 > UInt32.max { return 0 }
    return (min.integerValue + arc4random_uniform(UInt32(max.integerValue - min.integerValue + 1))).integerValue
}

randomNumber(UInt(10), UInt64(13))
randomNumber(UInt8(10), UInt32(13))
randomNumber(UInt16(10), UInt16(13))
randomNumber(UInt32(10), UInt8(13))
randomNumber(UInt64(10), UInt(13))

您至少需要创建 2 个函数:一个用于 SignedIntegerType,一个用于 UnsignedIntegerType

SignedIntegerType 具有类型强制函数:toIntMax()init(_: IntMax)

protocol _SignedIntegerType : _IntegerType, SignedNumberType {

    /// Represent this number using Swift's widest native signed integer
    /// type.
    func toIntMax() -> IntMax

    /// Convert from Swift's widest signed integer type, trapping on
    /// overflow.
    init(_: IntMax)
}

UnsignedIntegerType 也有类型强制函数:toUIntMax()init(_: UIntMax)

protocol _UnsignedIntegerType : _IntegerType {

    /// Represent this number using Swift's widest native unsigned
    /// integer type.
    func toUIntMax() -> UIntMax

    /// Convert from Swift's widest unsigned integer type, trapping on
    /// overflow.
    init(_: UIntMax)
}

使用这些功能,您可以:

func randomNumber<T: UnsignedIntegerType>(min: T, max: T) -> T {
    let n = max - min + 1
    let u = UInt32(n.toUIntMax())
    let r = arc4random_uniform(u)
    return T(r.toUIntMax()) + min
}

func randomNumber<T: SignedIntegerType>(min: T, max: T) -> T {
    let n = max - min + 1
    let u = UInt32(n.toIntMax())
    let r = arc4random_uniform(u)
    return T(r.toIntMax()) + min
}

但是,我们已经有了方便的 numericCast 内置函数:

func numericCast<T : _UnsignedIntegerType, U : _SignedIntegerType>(x: T) -> U
func numericCast<T : _SignedIntegerType, U : _UnsignedIntegerType>(x: T) -> U
func numericCast<T : _UnsignedIntegerType, U : _UnsignedIntegerType>(x: T) -> U
func numericCast<T : _SignedIntegerType, U : _SignedIntegerType>(x: T) -> U

numericCast 可以简化您的实施:

func randomNumber<T: UnsignedIntegerType>(min: T, max: T) -> T {
    return min + numericCast(arc4random_uniform(numericCast(max - min + 1)))
}

func randomNumber<T: SignedIntegerType>(min: T, max: T) -> T {
    return min + numericCast(arc4random_uniform(numericCast(max - min + 1)))
}

内部numericCastT转换为UInt32,外部UInt32转换为T

现在,这些函数具有完全相同的实现代码:)但我认为您不能将它们统一为一个函数。