以下功能定义为合法Swift:
func doSomething<T: StringProtocol>(value: T = "abc") {
// ...
}
编译器能够确定默认参数"abc"
是String
,并且String
符合StringProtocol
。
但是此代码无法编译:
func doSomething<T: Collection>(value: T = "abc") where T.Element == Character {
// ...
}
编译器错误:
类型'String'的默认参数值不能转换为类型'T'
似乎编译器将拥有与第一种情况一样多的信息来确定String
确实可转换为T
。此外,如果我删除默认参数并以相同的值调用该函数,则它会起作用:
doSomething(value: "abc")
此函数的编写方式可以不同,以便我可以提供默认的String
参数吗?这是Swift的局限性,还是我的思维模式的局限性?
答案 0 :(得分:2)
主要约束是T: ExpressibleByStringLiteral
。这就是允许从字符串文字中初始化某些内容的原因。
func doSomething<T: Collection>(value: T = "abc")
where T.Element == Character, T: ExpressibleByStringLiteral {
// ...
}
正如Leo Dabus指出的那样,T.Element == Character
在技术上不是必需的,但是删除它会改变含义。仅仅因为某物是一个集合并且可以用字符串文字初始化,并不意味着它的元素就是字符。
还值得注意的是,尽管所有这些都是可能的,但通常它是较差的Swift IMO。 Swift没有任何方法来表示默认的 type 是什么,因此doSomething()
在所有这些情况下都会导致“无法推断出通用参数'T'”。
正确的解决方案IMO是过载,它避免了所有这些问题:
func doSomething<T: StringProtocol>(value: T) {
}
func doSomething() {
doSomething(value: "abc")
}
这使您不仅可以使默认参数成为“可以用文字"abc"
初始化的东西”,而且还可以使您真正地知道:默认值是字符串“ abc”。
通常,默认参数只是重载的便利,因此您通常可以将任何默认参数替换为缺少该参数的显式重载。