【发布时间】:2019-09-17 16:56:10
【问题描述】:
与为参数指定协议相比,使用带有 where 子句的泛型有什么优势,如下面的函数签名?
func encode<T>(_ value: T) throws -> Data where T : Encodable {...}
func encode(value: Encodable) throws -> Data {...}
【问题讨论】:
与为参数指定协议相比,使用带有 where 子句的泛型有什么优势,如下面的函数签名?
func encode<T>(_ value: T) throws -> Data where T : Encodable {...}
func encode(value: Encodable) throws -> Data {...}
【问题讨论】:
第一个是泛型方法,需要符合Encodable 的具体类型。这意味着对于使用不同类型的每次调用encode,可能会创建一个全新的函数副本,并仅针对该具体类型进行优化。在某些情况下,编译器可能会删除其中的一些副本,但原则上encode<Int>() 是与encode<String>() 完全不同的函数。这是一个在编译时创建函数的(通用)系统。
相比之下,第二个是接受“可编码存在”类型参数的非泛型函数。存在是一个编译器生成的盒子,它包装了一些其他类型。原则上,这意味着值将在运行时在传递之前被复制到盒子中,如果它对于盒子来说太大,可能需要堆分配(同样,这可能不是因为编译器非常聪明,有时可以看到这是不必要的)。
协议名称和存在名称之间的这种歧义有望在未来得到解决(并且正在讨论这样做)。将来,后一个函数有望被拼写(注意“any”):
func encode(value: any Encodable) throws -> Data {...}
前者可能更快。函数的所有副本也可能需要更多空间。 (但关于编译器,请参见上文。不要假设您知道在实际的优化构建中哪些会更快。)
前者提供了一个真实的、具体的类型。这意味着它可以用于需要真实、具体类型的事情,例如调用静态方法或init。这意味着它可以在协议具有关联类型时使用。
后者被装箱成一个存在,这意味着它可以存储到异构集合中。前者只能放入其特定具体类型的集合中。
所以它们是完全不同的东西,每个都有它的目的。
【讨论】:
您可以使用多种类型约束。
func encode<T>(encodable: T) -> Data where T: Encodable, T: Decodable {
...
}
【讨论】: