Как принимать номера только для оператора, использующего Generics в Swift?

Я пытаюсь создать оператор для чисел. Например, оператор, который увеличивает число на 10.

Это код, который я написал:

prefix operator +++{}

prefix operator +++<T>(inout operand: T) -> T{
    operand += 10
    return operand
}

Есть ошибка с моим += оператор. это требует числовых операндов. поэтому я сделал это:

protocol Numeric {}

extension Int: Numeric {}
extension Float: Numeric {}
extension Double: Numeric {}

prefix operator +++ {}

prefix operator +++<T: Numeric>(inout operand: T) -> T {
    operand += 10
    return operand
}

Но это не удалось скомпилировать. У кого-нибудь есть идеи?

2 ответа

Решение

Проблема в том, что ваш Numeric Протокол не гарантирует += оператор будет присутствовать.

Учти это:

// Numeric imposes no requirements, so this will compile
extension Range: Numeric { }
// but Range has no += operator, so +++ could not work

Вместо этого вам придется добавить += как требование Numeric:

protocol Numeric: IntegerLiteralConvertible {
    func +=(inout lhs: Self,rhs: Self)
}

Обратите внимание, вам также нужно Numeric соответствовать IntegerLiteralConvertible так что вы можете создать 10 соответствующего типа, чтобы добавить к нему.

Теперь это компилируется и работает нормально, потому что Numeric гарантирует, что все функции, которые он использует, будут доступны:

prefix operator +++{}

prefix func +++<T: Numeric>(inout operand: T) -> T {
    operand += 10
    return operand
}

var i = 10
+++i  // i is now 20

Тем не менее, уже есть протокол, который делает то, что вам нужно: Strideable, которому соответствуют все стандартные числовые типы.

protocol Strideable {
// (actually _Strideable but don’t worry about that)

    /// A type that can represent the distance between two values of `Self`.
    typealias Stride : SignedNumberType
    // note, SignedNumberType conforms to IntegerLiteralConvertible

    /// Returns a `Self` `x` such that `self.distanceTo(x)` approximates
    /// `n`.
    ///
    /// - Complexity: O(1).
    ///
    /// - SeeAlso: `RandomAccessIndexType`'s `advancedBy`, which
    ///   provides a stronger semantic guarantee.
    func advancedBy(n: Self.Stride) -> Self
}

И реализация += который использует это:

func +=<T : Strideable>(inout lhs: T, rhs: T.Stride)

Это означает, что вы можете реализовать +++ как это:

prefix func +++<T: Strideable>(inout operand: T) -> T { 
    operand = operand.advancedBy(10)
    return operand 
}

Вот гораздо чище и лучше и работает со всем от Int8 в CGFloat и использует только стандартные типы библиотек, поэтому вам не нужно вручную соответствовать вашему собственному протоколу:

prefix operator +++ {}    
prefix func +++<T where T: FloatingPointType, T.Stride: FloatingPointType>(inout operand: T) -> T {
    operand = operand.advancedBy(T.Stride(10))
    return operand
}

prefix func +++<T where T: IntegerArithmeticType, T: IntegerLiteralConvertible, T.IntegerLiteralType: IntegerLiteralConvertible>(inout operand: T) -> T {
    operand = operand + T(integerLiteral: 10)
    return operand
}

Как указала @Airspeed Velocity, вы также можете сделать это так:

prefix operator +++ {}
prefix func +++<T: Strideable>(inout operand: T) -> T {
    operand = operand.advancedBy(10)
    return operand
}
Другие вопросы по тегам