我正在尝试构建一个MyStruct<T>可以使用Float或者Double用于内部存储的泛型类型.在初始化程序中,我传递了一个类型为T的参数(我打算将其作为Float或者Double).该初始化程序调用一些trig函数,如sin()和cos().这两个函数都在系统库中重载以提供Float和Double版本.
var f:Float=1.2
var d:Double=1.2
sin(f) //0.9320391
sin(d) //0.9320390859672263
Run Code Online (Sandbox Code Playgroud)
麻烦的是,我不能在我的通用结构中使用它们.精简案例如下所示:
struct MyStruct<T> {
var v:T
init(x:T){v=sin(x)}
}
Run Code Online (Sandbox Code Playgroud)
因为:
Playground执行失败:Untitled Page.xcplaygroundpage:9:17:error:无法使用类型'(T)'init(x:T){v = sin(x)}的参数列表调用'sin'
Untitled Page.xcplaygroundpage:9:17:注意:这些部分匹配的参数列表存在'sin'的重载:(Float),(Double)init(x:T){v = sin(x)}
看起来应该有办法让这项工作,但感觉很像这种情况.评论表明没有办法要求存在全球功能.
我可以通过使用如下构造强制这种情况:
init(x:T){v=T(sin(Double(x)))}
Run Code Online (Sandbox Code Playgroud)
并且对T施加约束,它可以从a构造Double,但这似乎打败了制作Float结构版本的目的,这是为了减少在关键代码循环中使用它时的计算工作量.
如果sin()在库中将其定义为通用函数而不是重载函数,感觉会更容易:
func sin<T:FloatingPointType> (x:T) -> T
Run Code Online (Sandbox Code Playgroud)
但是它就是这样啊.
有没有办法让我在标准库之上构建一个Float/Double不可知的库,而不会增加很多开销?
不幸的是,目前没有简单的方法可以sin()在Swift中实现.它必须被视为运算符(如何Equatable和==工作),以允许您将其添加为协议要求.
@ matt的解决方案是一个很好的快速解决方案,但是如果你想要更永久的东西,你可能需要考虑创建一个协议,然后扩展浮点类型,以便允许你sin()使用通用版本重载该函数.
protocol FloatingPointMathType : FloatingPointType {
var _sinValue : Self { get }
}
extension Float : FloatingPointMathType {
var _sinValue : Float {return sin(self)}
}
extension Double : FloatingPointMathType {
var _sinValue : Double {return sin(self)}
}
extension CGFloat : FloatingPointMathType {
var _sinValue : CGFloat {return sin(self)}
}
func sin<T:FloatingPointMathType>(x:T) -> T {return x._sinValue}
Run Code Online (Sandbox Code Playgroud)
(随意添加更多数学函数)
我们必须在这里使用'shadow'计算属性来弥补我们不能简单地sin()用作协议要求的事实.它并不理想 - 但可能和你一样好.
您现在可以继续使用sin()作为通用功能:
struct MyStruct<T:FloatingPointMathType> {
var v : T
init(x:T) {
v = sin(x)
}
}
Run Code Online (Sandbox Code Playgroud)
print(MyStruct(x: Float(3.0)).v) // 0.14112
print(MyStruct(x: Double(3.0)).v) // 0.141120008059867
print(MyStruct(x: CGFloat(3.0)).v) // 0.141120008059867
Run Code Online (Sandbox Code Playgroud)