IntegerType:无法使用类型为“T”的参数调用“init”

时间:2015-04-01 23:17:57

标签: swift generics numbers integer

我有这个函数在bounds中返回一个整数:

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作为参数

为什么不这样做?

3 个答案:

答案 0 :(得分:3)

问题在于UInt32没有init采用任意IntegerType。当然,它需要标准库中的每个定义的一个,但是如果某人实现了符合UInt128的{​​{1}}会怎么样?即使您在IntegerType中替换,在尝试将let u = UInt32(n.toIntMax())添加到r时也会遇到问题,因为再次没有min的实现会添加任意+ UInt32。考虑到溢出的可能性,这是有道理的 - 你知道IntegerType永远不会返回arc4random_uniform(u)大于UInt32,但Swift不能。您需要比Int8.max提供的更丰富的功能来编写具有正确的前置条件和后置条件的此函数的真正通用版本。

答案 1 :(得分:1)

您可以创建一个新的整数协议来接受任何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 :(得分:0)

您需要制作至少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,外部UInt32T转换为{{1}}。

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