【问题标题】:Why does the compiler not see the default code in a protocol?为什么编译器看不到协议中的默认代码?
【发布时间】:2019-11-05 13:46:25
【问题描述】:

编辑:我已经重申并希望通过here 澄清这个问题。现在我已经添加了解决方案。

我已经定义了一个函数(参见附件示例中的foo())作为structs 采用我的protocol 的默认函数。它应用了针对其他两个变量定义的 + 运算符,这些变量本身采用其他 protocols+ 在其中一个协议中定义。变量使用associatedtypes 输入。 我收到消息:

二元运算符“+”不能应用于“Self.PointType”和“Self.VectorType”类型的操作数

如果我在 struct 中实现该函数(参见附件中的 bar()),它可以工作,所以我确信我的 + 运算符可以工作。 我的例子被缩减到在操场上工作所需的最低限度。只需删除 LineProtocol extension 中的 cmets 即可获得错误。在我看来,Self.PointTypePointSelf.VectorTypeVector

明确一点:我使用associatedtypes 的原因是因为许多不同的structs 采用了示例中的三个协议中的每一个,所以我无法直接命名它们

public protocol PointProtocol {
   associatedtype VectorType: VectorProtocol
   var elements: [Float] { get set }
}

extension PointProtocol {
   public static func +(lhs: Self, rhs:VectorType) -> Self {
      var translate = lhs
      for i in 0..<2 { translate.elements[i] += rhs.elements[i] }
      return translate
   }
}

public protocol VectorProtocol {
   associatedtype VectorType: VectorProtocol
   var elements: [Float] { get set }
}

public struct Point: PointProtocol {
   public typealias PointType = Point
   public typealias VectorType = Vector
   public var elements = [Float](repeating: 0.0, count: 2)

   public init(_ x: Float,_ y: Float) {
      self.elements = [x,y]
   }
}

public struct Vector: VectorProtocol {
   public typealias VectorType = Vector
   public static let dimension: Int = 2
   public var elements = [Float](repeating:Float(0.0), count: 2)

   public init(_ x: Float,_ y: Float) {
      self.elements = [x,y]
   }
}

public protocol LineProtocol {
   associatedtype PointType: PointProtocol
   associatedtype VectorType: VectorProtocol
   var anchor: PointType { get set }
   var direction: VectorType { get set }
}

extension LineProtocol {
//   public func foo() -> PointType {
//      return (anchor + direction)
//   }
}

public struct Line: LineProtocol {
   public typealias PointType = Point
   public typealias VectorType = Vector
   public var anchor: PointType
   public var direction: VectorType

   public init(anchor: Point, direction: Vector) {
      self.anchor = anchor
      self.direction = direction
   }

   public func bar() -> Point {
      return (anchor + direction)
   }
}

let line = Line(anchor: Point(3, 4), direction: Vector(5, 1))
print(line.bar())
//print(line.foo())

根据@Honey 的建议改编的解决方案: 将扩展替换为:

extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
   public func foo() -> PointType {
      // Constraint passes VectorType thru to the PointProtocol
      return (anchor + direction)
   }
}

【问题讨论】:

  • 附带说明:您的类型命名非常混乱。 PointProtocolPointTypePoint。如果您查看 Apple 的 API 设计,他们会尝试限制相似的名称。如果他们没有明确的目的,那么也许你做错了什么
  • 对不起,这不是为了混淆。我正在构建的框架适用于几何,适用于点、向量、矩阵、线、平面等。在每种情况下,都有许多子类型,但 struct 不支持继承,所以我使用协议。例如,Point 系列有一个协议(PointProtocol)和几个结构(Point2D、Point3D、Point4D 等),要知道我指的是哪个,我提供了一个associatedtype PointType。相同的结构用于其他几何对象族。这就是理解命名的全部内容。

标签: swift swift-protocols associated-types default-implementation


【解决方案1】:

我知道问题出在哪里。不确定我的解决方案是否是最佳答案。

问题在于您的两个关联类型本身都有关联类型。

所以在扩展中,Swift 编译器无法确定关联类型的类型——除非你对其进行约束。

喜欢做:

extension LineProtocol where Self.VectorType == Vector, Self.PointType == Point {
    public func foo() -> Self.PointType {
      return (anchor + direction)
   }
}

您的代码适用于您的具体类型Line,因为您的两个关联类型都满足了它们的要求,即:

public typealias PointType = Point // makes compiler happy!
public typealias VectorType = Vector  // makes compiler happy!

FWIW,您本可以摆脱与您的 associatedtype 要求的显式一致性,让编译器推断1 符合您的 associatedtypes 要求,并这样编写您的 Line 类型:

public struct Line: LineProtocol {

   public var anchor: Point
   public var direction: Vector

   public init(anchor: Point, direction: Vector) {
      self.anchor = anchor
      self.direction = direction
   }

   public func bar() -> Point {
      return (anchor + direction)
   }
}

1:Generics - Associated Types

感谢 Swift 的类型推断,您实际上不需要声明 Int 的具体项目作为 IntStack 定义的一部分。因为 IntStack 符合 Container 的所有要求 协议,Swift 可以推断出要使用的适当项目,只需通过 查看 append(_:) 方法的 item 参数的类型和 返回下标的类型。事实上,如果你删除 typealias Item = 上面代码中的 int 行,一切仍然有效,因为很清楚应该为 Item 使用什么类型。

【讨论】:

  • 嗨,亲爱的。感谢您的快速回复。但是,它不会解决问题,因为我使用协议的原因是有许多structs 符合向量、点和线协议中的每一个,所以我需要传入特定的试图使用它。例如,不能将 2 维中的线添加到 3 维中的点。实际上有 Point2、Point3、Vector3 结构等等......
  • 明白。您对问题的描述和我的不好的解决方案是否清楚?
  • 我认为你可能是正确的说它是导致问题的两个协议中的关联类型,但我不明白为什么
  • 我根本不使用关联类型@hamish 可以更好地解释。但是,associatedtype PointType: PointProtocol 和后面的行是问题所在。一旦进入你的扩展,编译器就会环顾四周。它会看到anchorPointProtocol 类型,所以它会查看那个类型,然后编译器就像“把它放在那里!不要那么快。PointProtocol 也有一个associatedtype,什么是那是什么类型?PointProtocol 好的!让我研究一下。还有一个名为 VectorType 的关联类型的无赖,你希望它是什么类型“
  • 所以按照同样的想法,我重命名了LineProtocol 中的关联类型以避免这种冲突,但这并没有帮助。实际上,anchor 将具有特定的类型(例如示例中的 Point),并且 Point 告诉 PointProtocol 他们需要什么 VectorType(例如示例中的 Vector),因此应该没有歧义。请注意,Bar() 函数解析类型没有困难。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-15
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
相关资源
最近更新 更多