Pointers 分配块指针:Objective-C与C+之间的差异+;班级 我发现分配块在Objtovi-C类参数和C++类参数方面的行为不同。

Pointers 分配块指针:Objective-C与C+之间的差异+;班级 我发现分配块在Objtovi-C类参数和C++类参数方面的行为不同。,pointers,clang,subclass,objective-c-blocks,objective-c++,Pointers,Clang,Subclass,Objective C Blocks,Objective C++,假设我有一个简单的Objective-C类层次结构: @interface Fruit : NSObject @end @interface Apple : Fruit @end 然后我可以写这样的东西: Fruit *(^getFruit)(); Apple *(^getApple)(); getFruit = getApple; 这意味着,对于Objective-C类,块的返回类型是协变的:返回更具体的块可以被视为返回更一般的块的“子类”。在这里,可以将交付苹果的getApple块安全

假设我有一个简单的Objective-C类层次结构:

@interface Fruit : NSObject
@end

@interface Apple : Fruit
@end
然后我可以写这样的东西:

Fruit *(^getFruit)();
Apple *(^getApple)();
getFruit = getApple;
这意味着,对于Objective-C类,块的返回类型是协变的:返回更具体的块可以被视为返回更一般的块的“子类”。在这里,可以将交付苹果的
getApple
块安全地分配给
getFruit
块。事实上,如果以后使用,当您期待一个
水果*
时,总是可以保存以接收
苹果*
。从逻辑上讲,相反的方法是行不通的:
getApple=getFruit
不会编译,因为当我们真的想要一个苹果时,我们不喜欢只得到一个水果

同样,我可以这样写:

void (^eatFruit)(Fruit *);
void (^eatApple)(Apple *);
eatApple = eatFruit;
这表明块的参数类型是协变的:可以处理更一般的参数的块可以用于需要处理更具体的参数的块。如果一个街区知道如何吃水果,它也会知道如何吃苹果。同样,反之亦然,这将无法编译:
eatFruit=eataapple

<>这是好的,而且是客观的。现在让我们尝试C++或ObjuleC++,假设我们有类似的C++类:

class FruitCpp {};

class AppleCpp : public FruitCpp {};

class OrangeCpp : public FruitCpp {};
遗憾的是,这些块分配不再编译:

 FruitCpp *(^getFruitCpp)();
 AppleCpp *(^getAppleCpp)();
 getFruitCpp = getAppleCpp; // error!

 void (^eatFruitCpp)(FruitCpp *);
 void (^eatAppleCpp)(AppleCpp *);
 eatAppleCpp = eatFruitCpp; // error!
Clang抱怨“从不兼容类型分配”错误。因此,对于C++类,块在返回类型和参数类型.<
为什么呢?我用Objto-C类做的同样的论点也不适用于C++类吗?我遗漏了什么?

该功能可能被忽略了。有一些显示出Calang-Primes的人关心Objto-C++中Objy-C类型的协方差和逆变工作,但对于C++本身我找不到任何东西。无论是C++还是ObjultC.</P> < P>,都没有提及协方差或逆变,这是由于Objy-C和C++对象模型的差异造成的。特别是,给定一个指向Objective-C对象的指针,可以将该指针转换/强制转换为指向基类或派生类的指针,而无需实际更改指针的值:不管怎样,对象的地址都是相同的

<>因为C++允许多个和虚拟继承,C++对象不是这样的:如果我有一个指向C++类的指针,我把这个指针转换成一个基类或派生类的指针,我可能不得不调整指针的值。例如,考虑:

class A { int x; }
class B { int y; }
class C : public A, public B { }

B *getC() { 
  C *c = new C;
  return c;
}
假设getC()中的新C对象被分配到地址0x10。指针“c”的值为0x10。在return语句中,指向C的指针需要调整为指向C中的B子对象。因为B在C的继承列表中位于A之后,它(通常)将放在A之后的内存中,因此这意味着添加4字节的偏移量( ==sizeof(A))指向指针,因此返回的指针将为0x14。类似地,将B*强制转换为C*将从指针中减去4个字节,以说明C中B的偏移量。在处理虚拟基类时,想法是相同的,但偏移量不再是已知的编译时常量:它们在执行期间通过vtable访问

现在,考虑一下这对分配的影响:

C (^getC)();
B (^getB)();
getB = getC;
getC块返回指向C的指针。要将其转换为返回指向B的指针的块,我们需要通过添加4个字节来调整从块的每次调用返回的指针。这不是对区块的调整;这是对块返回的指针值的调整。可以通过合成一个新块来实现这一点,该新块包裹前一块并执行调整,例如

getB = ^B() { return getC() }
这是可以在编译器中实现的,编译器在使用需要调整的协变返回类型重写虚拟函数时已经引入了类似的“thunks”。但是,对于块,它会导致另一个问题:块允许与==进行相等比较,因此要评估“getB==getC”,我们必须能够查看赋值“getB=getC”生成的thunk来比较底层块指针。同样,这是可以实现的,但是需要一个更重的blocks运行时,它能够创建(uniqued)thunk,能够对返回值(以及任何逆变参数)执行这些调整。虽然所有这些在技术上都是可能的,但成本(在运行时大小、复杂性和执行时间方面)超过了好处


回到Objective-C,单继承对象模型永远不需要对对象指针进行任何调整:不管指针的静态类型如何,只有一个地址指向给定的Objective-C对象,因此协方差/反协方差永远不需要任何thunk,而块赋值是一个简单的指针赋值(+ +块拷贝/弧块在ARC下的释放)。

最可能的是,忽略了特征。有人指出在Objto-C++中,对于Objtovi-C类型,我们要做协方差和逆变工作,但是我找不到任何C++本身。也不提。我应该把它放在什么地方(在哪里?)作为错误/功能请求?您可以为LLVM项目提交错误和功能请求(需要免费注册有效的电子邮件,就像大多数公共错误跟踪程序一样),但预计至少会延迟几个月。如果你真的喜欢,如果你想自己制作补丁,邮件列表上的人可能会很乐意帮助你。我去年9月报告了ARC的一个bug,并在一开始就得到了承认