让Swift泛型可以玩重载函数 [英] Making Swift generics play with overloaded functions

查看:90
本文介绍了让Swift泛型可以玩重载函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图建立一个通用类型 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()

  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 either Float or Double for internal storage. In the initializer, I'm passing an argument of type T (which I intend to be either Float or Double). That initializer calls some trig functions such as sin() and cos(). Both of those functions are overloaded in the system libraries to provide Float and Double 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 the Float 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 how Equatable 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆