我试图在Swift 5.7中为任何数值(à la micrograd)创建一个通用 Package 器。到目前为止,我的实现是这样的:
struct Value<Scalar: SignedNumeric>: Equatable {
var data: Scalar
init(_ data: Scalar) {
self.data = data
}
}
// MARK: Value+SignedNumeric
extension Value: SignedNumeric {
typealias IntegerLiteralType = Scalar.IntegerLiteralType
typealias Magnitude = Scalar.Magnitude
var magnitude: Magnitude {
data.magnitude
}
init(integerLiteral value: IntegerLiteralType) {
self.data = Scalar(integerLiteral: value)
}
init?<T>(exactly source: T) where T : BinaryInteger {
if let numericData = Scalar(exactly: source) {
self.data = numericData
} else {
return nil
}
}
static func * (lhs: Value<Scalar>, rhs: Value<Scalar>) -> Value<Scalar> {
return Value(lhs.data * rhs.data)
}
static func + (lhs: Value<Scalar>, rhs: Value<Scalar>) -> Value<Scalar> {
return Value(lhs.data + rhs.data)
}
static func - (lhs: Value<Scalar>, rhs: Value<Scalar>) -> Value<Scalar> {
return Value(lhs.data - rhs.data)
}
static func *= (lhs: inout Value<Scalar>, rhs: Value<Scalar>) {
lhs.data = lhs.data * rhs.data
}
}
我遇到的问题是,如果我只提供一个没有任何显式类型注解的整数字面量,下面的代码将无法编译:
func testScalarTypeInference() {
// all of these compile fine
Value(Double(1.0))
Value<Double>(1)
Value<Int>(1)
Value(Int(1))
Value(1 as Int)
Value(1.0)
// doesn't compile
Value(1) // Generic parameter 'Scalar' could not be inferred in cast to 'Value'
}
为什么编译器会推断出简单的Double
文字(Value(1.0)
)的类型,而不是Int
文字(Value(1)
)的类型?
编辑:最让人困惑的地方我忘了说了,这个问题只在Value
尝试符合SignedNumeric
时存在。下面的代码编译正常:
struct ValueWrapper<Scalar: SignedNumeric> {
var data: Scalar
init(_ data: Scalar) {
self.data = data
}
}
func testValueWrapperTypeInference() {
type(of: ValueWrapper(1)) // Expression of type 'ValueWrapper<Int>.Type' is unused
type(of: ValueWrapper(1.0)) // Expression of type 'ValueWrapper<Double>.Type' is unused
}
所以我想可能是其中一个init
出了问题?
4条答案
按热度按时间1cosmwyk1#
它实际上比SignedNumeric简单。对于ExpressibleByIntegerLiteral(SignedNumeric需要)也存在同样的问题:
我不认为这是可能的建立你试图建立什么。(ExpressibleByIntegerLiteral上的
init(_ value: Self)
扩展可能存在歧义,这可能是它将其称为“强制转换”的原因。)如果您需要此功能,我建议打开bug report。我不相信可以将ExpressibleByIntegerLiteral创建为泛型 Package 器,因为您不能默认Scalar类型,这会妨碍你遵守SignedNumericSR-7476可能是相关的。
gojuced72#
为什么编译器会推断出简单的
Double
文字(Value(1.0)
)的类型,而不是Int
文字(Value(1)
)的类型?我想是因为
SignedNumeric
继承了ExpressibleByIntegerLiteral
,但它没有继承ExpressibleByFloatLiteral
。当编译器看到一个带有小数点的文字时,例如
1.5
,除非可以从上下文推断出浮点类型,否则它会假设Double
,然后在这种情况下,它可以说Scalar
必须是Double
。这是一个特殊情况,因此人们不必明确let f = 1.5
中f
的类型。对于整数文字也有类似的规则,但不幸的是,它不适用,因为任何
ExpressibleByIntegerLiteral
都会覆盖该规则,即使没有,如果有两个Scalar
类型,其ExpressibleByIntegerLiteral
的实现使用int
作为关联类型,则无法在它们之间进行选择。至少我是这么想的。
kb5ga3dv3#
编辑
实际上,这是令人困惑的,在阅读this answer之后,我尝试符合
ExpressibleByFloatLiteral
,这次ValueWrapper(1.0)
触发了错误:所以我认为这个问题来自于
init(_ value: Self)
* 的一些模糊性更合理。原创理论
如果你看
ExpressibleByFloatLiteral.FloatLiteralType
:讨论
FloatLiteralType的有效类型为Float、Double和Float80(如果可用)。
对于
ExpressibleByIntegerLiteral.IntegerLiteralType
,您可以:讨论
标准库整型和浮点类型都是IntegerLiteralType的有效类型。
例如,当你从一个字面量初始化一个浮点数时,它只能是一个浮点数,默认值是
Double
,但是当你使用一个整数字面量时,它可能是一个整数或浮点数,编译器会感到困惑。wlsrxk514#
不知道为什么会失败(可能是与其他语言特性或其他构造函数冲突),但如果将调用从
Value(1)
更改为Value.init(1)
,它就开始工作了。另一种解决方法是在构造函数Value(data: 1)
中使用命名参数。或者像这样键入: