Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 您能否强制使用两种类型的泛型以确保这些类型不相同?_Swift_Generics_Where Clause_Swift5 - Fatal编程技术网

Swift 您能否强制使用两种类型的泛型以确保这些类型不相同?

Swift 您能否强制使用两种类型的泛型以确保这些类型不相同?,swift,generics,where-clause,swift5,Swift,Generics,Where Clause,Swift5,是否可以限制以下泛型以确保T1和T2不是同一类型 下面是我试图做的(但它不会编译) 这些应该是有效的 let w = Foo<A, B>() let x = Foo<B, A>() 让w=Foo() 设x=Foo() 由于Swift的泛型不执行类型擦除,我认为这也是允许的(但如果不执行也可以。这只是为了讨论中的“完整性”) 让y=Foo 然而,这是不允许的 let y = Foo<A, A> 让y=Foo 现在,我可以在构造函数中进行检查以确保情况属

是否可以限制以下泛型以确保T1和T2不是同一类型

下面是我试图做的(但它不会编译)

这些应该是有效的

let w = Foo<A, B>()
let x = Foo<B, A>()
让w=Foo()
设x=Foo()
由于Swift的泛型不执行类型擦除,我认为这也是允许的(但如果不执行也可以。这只是为了讨论中的“完整性”)

让y=Foo
然而,这是不允许的

let y = Foo<A, A>
让y=Foo
现在,我可以在构造函数中进行检查以确保情况属实,但我想知道是否可以在编译时使用“where”子句进行检查,但我一直没有成功


如果有必要,可以使用Swift 5。

这里是一种可能的方法,编译时为相同的类型生成错误,并为不同的类型传递错误

使用:Xcode 11.2/Swift 5.1.2进行测试

class A{}
B类{}
福班{
私有init(){}
}
扩展Foo,其中T1:A,T2:B{
便利初始化(a:T1,b:T2){
self.init()
}
}
扩展Foo,其中T1:B,T2:A{
便利初始化(b:T1,a:T2){
self.init()
}
}
和测试

让test1=Foo(A(),B())
设test2=Foo(B(),A())

让test2=Foo(A(),A())//我不知道描述这一点的正确数学术语是什么,但据我所知,Swift中的所有输入都是“相加的”。您可以设置类型的“和”(
T1&T2
),您可以要求精确的类型(
T1==T2
),添加子类型关系(
T1:T2
),但您不能执行任何“否定”操作。你不能说
T1!=Int
T1不是T2的子类,等等。。。。。。从根本上说,类型只是受支持操作集上的标签。向类型变量添加约束会减少可能适用的类型的数量,但会增加类型变量的实用性(通过确保更多操作可用)。通过说
T:CustomStringConvertible
,您知道您可以调用它的
var description:String
。另一方面,说
T not subtype of CustomStringConvertible
没有什么好处。这就引出了一个问题:如果存在这样一个功能,你想用它来表达什么?我相当肯定这个问题的答案是否定的,但如果你拥有它,你会用它做什么?@Alexander,这通常是不正确的。例如,幻影类型允许使用具有相同结构的类型来区分其他类型。SwiftUI经常这样做(特别是PreferenceKey)。我想你已经读过了,但是对于任何关注这一点的人来说,最好的讨论是“协议不仅仅是语法包”@RobNapier我想可能是
T1!=T2
表单可以利用类型的“语义容器”角色(与其“概念角色”相反)来添加某种值,但我真的想不出任何这样的例子。这感觉有点错误。在test1案例中,您将如何实际传递参数?你能试试这个,但实际上分配属性吗?谁说应该有属性?它可以是委托、计算、保理等。。。属性是一个不同的问题。我的意思是,实际上在init中使用
a
b
做任何事情。我相信这个解决方案依赖于这样一个事实:它们被忽略了。如果它们没有被忽略,我不确定它是否仍然有效。如果根本没有参数呢?想想
var foo=foo()
。这不会产生任何编译时问题。没关系。。。我想我只需要使用一个可释放的初始值设定项就可以了。要禁止像
Foo()
这样的情况,可以将默认构造函数私有化为
private init(){}
,这样这些情况也会生成编译器错误('Foo'初始值设定项由于“private”保护级别而无法访问)
let w = Foo<A, B>()
let x = Foo<B, A>()
let y = Foo<A, SubA>
let y = Foo<A, A>
class A {}
class B {}

class Foo<T1, T2> {
    private init() {}
}

extension Foo where T1: A, T2: B {
    convenience init(_ a: T1, _ b: T2) {
        self.init()
    }
}

extension Foo where T1: B, T2: A {
    convenience init(_ b: T1, _ a: T2) {
        self.init()
    }
}
let test1 = Foo(A(), B())
let test2 = Foo(B(), A())
let test2 = Foo(A(), A()) // << Error: Ambiguous reference to initializer 'init(_:_:)'
let test2 = Foo(B(), B()) // << Error: Ambiguous reference to initializer 'init(_:_:)'
let test3 = Foo<String, String>() // Error: 'Foo<T1, T2>' initializer is inaccessible due to 'private' protection level