Подтвердить что ты не робот

Возникли проблемы с возвратным типом BOOL в блоках Objective-C

Я наткнулся на любопытную проблему с типом возврата BOOL в блоках. Имея следующее определение:

typedef BOOL (^BoolBlock)(void);

... этот код проходит:

BoolBlock foo = ^{ return YES; };

... но это не скомпилируется:

BoolBlock bar = ^{ return YES || NO; };

Со следующим сообщением об ошибке:

Несовместимые типы указателей блоков инициализация "BoolBlock" (он же BOOL (^) (void) ') с выражением типа 'int (^) (void)'

Я могу решить проблему с использованием явного приведения, но не должно ли это работать без нее? Есть ли лучшее решение?

4b9b3361

Ответ 1

Вероятно, вы думаете, что оператор || работает на таких языках, как Ruby и Python, где он возвращает в первом операнде, который является правдивым. В C он возвращает 1, если либо операнд правдивый, либо 0 - это то, почему он думает, что вы возвращаете целое число.

Ответ 2

|| оператор возвращает тип int, как сказал Чак.

BoolBlock bar = ^{ return (BOOL)(YES || NO); };

или

BoolBlock bar = ^BOOL (void){ return YES || NO; };
BoolBlock bar = ^BOOL (){ return YES || NO; }; // warns in gcc, ok with clang

Ответ 3

Как утверждали другие, причина, по которой вы получаете ошибку, заключается в том, что e0 || e1 возвращает int независимо от типов e0 и e1. Поскольку компилятор отображает тип возврата блока, основанный на операторах return, у вас есть блок, который возвращает int, и вы пытаетесь назначить его блочной переменной, тип возврата блока которой BOOL.

Я лично предпочитаю этот синтаксис:

BoolBlock bar = ^BOOL { return YES || NO };

чтобы избежать ошибки, указав, что тип возврата блока BOOL. Значение rvalue, блок-литерал, понимается как блок, тип возврата которого BOOL, и компилятор применяет обычные преобразования C.

Относительно того, почему это происходит, это дизайнерское решение, хотя оно явно не документировано. 1 Блоки - это новая языковая функция. Дизайнеры-компиляторы 2 решили, что они должны иметь более строгую семантику по блокам - а именно, назначение типов указателей блоков должно строго соответствовать типам - и они применяют эту более жесткую семантику при назначении блока блочной переменной независимо от того, что rvalue является указателем блока или блочным литералом.

Так как стандарты ISO/IEC не перекрывают блоки на C или С++, разработчики компиляторов могут принимать эти решения. Apple представила блоки для ISO/IEC JTC1/SC22/WG14 как WG14/N1370 и WG14/N1451, и, если они его принимают, это поведение (или его вариант) должно быть стандартизировано и документировано.

1 В исходном коде Clangs есть комментарий, в котором указано, что назначение указателей блоков более строгое, чем назначение указателей на функции.

2 Я лично спросил их об этом.