考虑以下内容(从实际代码中简化):
func roundedInt<T: FloatingPoint>(_ f: T) -> Int {
return Int(f.rounded())
}
无法使用以下错误进行编译:
无法使用类型的参数列表调用类型为“Int”的初始值设定项 '(T)''Int'的过载与这些部分匹配存在 参数列表:(Int64),(Word),(UInt8),(Int8),(UInt16),(Int16), (UInt32),(Int32),(UInt64),(UInt),(Int),(Float),(Double), (Float80),(String,radix:Int),(CGFloat),(NSNumber)
我认为这意味着FloatingPoint无法与任何Int重载匹配。
有一种简单的方法可以使这项工作吗?或者是否存在限制Swift泛型(例如,与C ++模板相比)排除这种情况?
答案 0 :(得分:3)
这是我能想到的最好的:
func roundedInt<T: FloatingPoint>(_ f: T) -> Int {
return Int(Float80("\(f.rounded())")!)
}
基本上,这是一种“欺骗”。据我所知,标准库中符合FloatingPoint
的所有类型都是Float
,Double
和Float80
。它们的字符串表示格式相同。这就是我将字符串表示转换为Float80
的原因。然后我将此号码转换为Int
。
我说这是一个骗子,因为它不适用于大数字。当您将非常大的数字放入此函数时,会发生致命错误。经过一些反复试验,我发现在64位处理器上传递给此函数的最大数字是9223372036854775295,比Int.max
小512。
此外,如果您在那里传递9223372036854775295,它将返回9223372036854770000而不是原始数字。这很可能是因为浮点数不准确。
答案 1 :(得分:2)
简单C ++模板只不过是 macro-ish ,类型少,源代码替换机制。调用代码将替换为应用的模板,编译器会检查生成的代码是否有意义。你是对的,roundedInt<T>
无界函数应该可以在C ++版本中正常工作。
Swift泛型是设计上的类型安全,这意味着通用代码必须独立于声音,与给定呼叫站点的任何细节无关。在您的示例中,FloatingPoint
协议是指导编译的类型(而不是调用代码使用的实际T
类型)。
(顺便说一下,Java / C#generics也类似于Swift风格。)
关于您的实际问题,您可以简单地为roundedInt
函数提供两个重载:
func roundedInt(_ f: Float) -> Int { ... }
func roundedInt(_ f: Double) -> Int { ... }
应涵盖大多数用例。当然,假设您只使用此编写小辅助函数...而不是完整的库/框架!
否则,请尝试 @Sweeper 基于字符串的解决方案。但请记住,除了他正确注意到的潜在精度损失之外,还有潜伏在那里的一些令人讨厌的性能问题。
答案 2 :(得分:0)
使用BinaryFloatingPoint
这可以按照您的预期工作,在Int可以表示的范围内。
func roundedInt<T: BinaryFloatingPoint>(_ f: T) -> Int {
return Int(f.rounded())
}
let x = roundedInt(Float(42.1)) // 42