1

我正在尝试编写一个辅助函数,它将位索引数组转换为符合 OptionSet 的类。

func getOptionSet<T: OptionSet>(bitIndexes: [Int64]) -> T {
    var result: Int64 = 0
    for index in bitIndexes {
        result |= 1 << index
    }
    return T(rawValue: result) // error
}

这无法编译:

Cannot invoke initializer for type 'T' with an argument list of type '(rawValue: Int64)'

我也尝试过使用 RawValue:

func getOptionSet<T: OptionSet>(bitIndexes: [T.RawValue]) {
    var result = T.RawValue()  // error

这也不起作用:

Cannot invoke value of type 'T.RawValue.Type' with argument list '()'

这可以做到吗?我需要在 T 上添加额外的约束吗?

我知道可以重写此函数以使用具体类型,但如果可能,我想保持它的通用性。

4

2 回答 2

4

您的代码中的问题是Int64并且T.RawValue是不相关的并且可以是不同的类型。

但是每个无符号整数类型都可以从 和 转换为UIntMax,因此可以通过限制RawValue来解决问题UnsignedInteger

使用@OOPer 的想法来定义一个自定义初始化程序,这将是:

extension OptionSet where RawValue: UnsignedInteger {
    init(bitIndexes: [Int]) {
        var result: UIntMax = 0
        for index in bitIndexes {
            result |= 1 << UIntMax(index)
        }
        self.init(rawValue: RawValue(result))
    }
}

也可以写成

extension OptionSet where RawValue: UnsignedInteger {
    init(bitIndexes: [Int]) {
        let result = bitIndexes.reduce(UIntMax(0)) {
            $0 | 1 << UIntMax($1)
        }
        self.init(rawValue: RawValue(result))
    }
}

到目前为止,我看到的所有选项集类型都有一个无符号整数类型作为原始值,但请注意,同样适用于 SignedIntegerand IntMax

例子:

struct TestSet: OptionSet {
    let rawValue: UInt16
    init(rawValue: UInt16) {
        self.rawValue = rawValue
    }
}

let ts = TestSet(bitIndexes: [1, 4])
print(ts) // TestSet(rawValue: 18)

也比较一下如何在 Swift 中枚举 OptionSetType?对于反向任务。


更新:Swift 4开始 ,该UnsignedInteger协议有一个

public static func << <RHS>(lhs: Self, rhs: RHS) -> Self where RHS : BinaryInteger

方法,这样上面的代码就可以简化为

extension OptionSet where RawValue: UnsignedInteger {
    init(bitIndexes: [Int]) {
        self.init(rawValue: bitIndexes.reduce(0) { $0 | 1 << $1 })
    }
}

无需中间转换为“最大”整数类型。

于 2016-07-15T14:01:20.193 回答
3

您可能需要更多设置才能使您getOptionSet的工作:

protocol OptionBitShiftable: IntegerLiteralConvertible {
    func << (lhs: Self, rhs: Self) -> Self
    func |= (lhs: inout Self, rhs: Self)
}
extension Int64: OptionBitShiftable {}
extension UInt64: OptionBitShiftable {}
//...

有了上面这些,你可以getOptionSet这样写:

func getOptionSet<T: OptionSet where T.RawValue: OptionBitShiftable>(bitIndexes: [T.RawValue]) -> T {
    var result: T.RawValue = 0
    for index in bitIndexes {
        result |= 1 << index
    }
    return T(rawValue: result)
}

用法:

struct MyOptionSet: OptionSet {
    var rawValue: Int64
    init(rawValue: Int64) {
        self.rawValue = rawValue
    }
}
let myOption: MyOptionSet = getOptionSet(bitIndexes: [1,2,3,5,7])
print(myOption.rawValue) //->174(=2+4+8+32+128)

或者,您可以像这样定义一个初始化程序:

extension OptionSet where RawValue: OptionBitShiftable {
    init(bitIndexes: [RawValue]) {
        var result: RawValue = 0
        for index in bitIndexes {
            result |= 1 << index
        }
        self.init(rawValue: result)
    }
}

您可以将其用作:

let alsoMyOption = MyOptionSet(bitIndexes: [4, 6])
print(alsoMyOption.rawValue) //->80(=16+64)
于 2016-07-15T13:34:15.410 回答