让Swift泛型可以玩重载函数 [英] Making Swift generics play with overloaded functions
问题描述
我试图建立一个通用类型 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
麻烦是,我无法在我的通用中使用它们结构。剥离下来的案例如下所示:
struct MyStruct< T> {
$因为:
var v:T
init(x:T){v = sin(x)}
}
Playground执行失败:无标题Page.xcplaygroundpage:9: 17:
错误:无法用'(T)'类型的参数列表调用'sin'
init(x:T){v = sin(x)}
无标题Page.xcplaygroundpage:9:17:注意:'sin'的重载与这些部分匹配的参数列表一起存在:
(Float),(Double)
init(x: T){v = sin(x)}
似乎应该有一种方法来完成这项工作,但它感觉很像这种情况。如果评论表明无法要求存在全局函数。
我可以通过使用如下构造来强制这种情况:
init(x:T){v = T(sin(Double(x)))}
并在T上设置一个约束条件,它可以从
Double
构造,但这似乎失败了这个结构的Float
版本的目的是为了在关键代码循环中减少计算工作量。
如果在库中将
sin()
定义为泛型函数而不是重载函数,那感觉这样会更容易:func sin< T:FloatingPointType> (x:T)→> T
但它就是这样。
有没有办法让我在标准库之上建立一个Float / Double不可知的库,而不会增加很多开销?
解决方案不幸的是,目前没有简单的方法来执行
sin()迅速。它必须被视为一个操作符(比如 Equatable
和==
的工作原理),以便让您将其作为协议要求添加。
@ matt的解决方案是一个很好的快速修复,但是如果你想要更长久的东西,你可能需要考虑创建一个协议,然后扩展浮点类型,以便让你重载
sin()$ c $使用通用版本的函数。
protocol FloatingPointMathType:FloatingPointType {
var _sinValue:Self {get}
扩展名浮点数:FloatingPointMathType {
var _sinValue:Float {return sin(self)}
}
扩展名Double:FloatingPointMathType {
var _sinValue:Double {return sin(self)}
}
extension CGFloat:FloatingPointMathType {
var _sinValue:CGFloat {return sin(self)}
}
func sin< T:FloatingPointM athType(x:T) - > T {return x._sinValue}
(随意添加更多数学函数) sup>
我们不得不在这里使用'shadow'计算属性来弥补我们不能简单地使用
sin ()
作为协议要求。这是不理想的 - 但可能与你将要得到的一样好。
现在你可以继续使用
sin() code>作为一个通用函数:
struct MyStruct< T:FloatingPointMathType> {
var v:T
init(x:T){
v = sin(x)
}
}
<$ c $ MyPrint(MyStruct(x:Double(3.0))。v)// 0.141120008059867
print(MyStruct(x) :CGFloat(3.0))。v)// 0.141120008059867
I'm trying to build a generic type
MyStruct<T>
that can use eitherFloat
orDouble
for internal storage. In the initializer, I'm passing an argument of type T (which I intend to be eitherFloat
orDouble
). That initializer calls some trig functions such assin()
andcos()
. Both of those functions are overloaded in the system libraries to provideFloat
andDouble
versions.var f:Float=1.2 var d:Double=1.2 sin(f) //0.9320391 sin(d) //0.9320390859672263
Trouble is, I can't use them in my generic struct. A stripped down case would look like this:
struct MyStruct<T> { var v:T init(x:T){v=sin(x)} }
Because:
Playground execution failed: Untitled Page.xcplaygroundpage:9:17: error: cannot invoke 'sin' with an argument list of type '(T)' init(x:T){v=sin(x)}
Untitled Page.xcplaygroundpage:9:17: note: overloads for 'sin' exist with these partially matching parameter lists: (Float), (Double) init(x:T){v=sin(x)}
It seems like there should be a way to make this work, but it feels a lot like this situation. Where the comments suggest there is no way to require the existence of a global function.
I can force the situation by using a construct such as:
init(x:T){v=T(sin(Double(x)))}
and putting a constraint on T that it can be constructed from a
Double
, but that seems to defeat the purpose of making theFloat
version of the struct, which is to reduce computation effort when this is used in a critical loop of code.It feels like this would have been easier if
sin()
had been defined in the library as a generic function rather than an overloaded function:func sin<T:FloatingPointType> (x:T) -> T
but it is what it is.
Is there a way for me to build a library that is Float/Double agnostic on top of the standard libraries without adding a lot of overhead?
解决方案Unfortunately, there's no easy way to do this the way
sin()
is currently implemented in Swift. It would have to be treated as an operator (like howEquatable
and==
works) in order to allow you to add it as a protocol requirement.@matt's solution is a nice quick fix, but if you want something more permanent, you may want to consider creating a protocol and then extending the floating point types in order to allow you to overload the
sin()
function with a generic version.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}
(Feel free to add more math functions)
We're having to use a 'shadow' calculated property here to make up for the fact that we can't simply use
sin()
as a protocol requirement. It's not ideal – but probably about as good as you're going to get.You can now go ahead and use
sin()
as a generic function:struct MyStruct<T:FloatingPointMathType> { var v : T init(x:T) { v = sin(x) } }
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
这篇关于让Swift泛型可以玩重载函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!