Зачем вам использовать тернарный оператор без присвоения значения для "истинного" условия (x = x?: 1)
В коде qemu с открытым кодом Android с открытым кодом я столкнулся с этой строкой кода:
machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */
Это просто путаный способ сказать:
if (machine->max_cpus) {
; //do nothing
} else {
machine->max_cpus = 1;
}
Если да, то не было бы яснее:
if (machine->max_cpus == 0) machine->max_cpus = 1;
Интересно, что это компилируется и отлично работает с gcc, но не компилируется на http://www.comeaucomputing.com/tryitout/.
Ответы
Ответ 1
Это разрешено в GNU как неясное расширение до C
5.7 Условные обозначения с опущенными операндами
Средний операнд в условном выражение может быть опущено. Тогда, если первый операнд отличен от нуля, его значение равно значение условного выражение.
Следовательно, выражение
x ? : y
имеет значение x, если оно отличное от нуля; в противном случае значение y.
Этот пример совершенно эквивалентен к
x ? x : y
В этом простом случае способность к опустить средний операнд не особенно полезно. Когда это становится полезно, когда первый операнд делает, или может (если это аргумент макроса), содержат побочный эффект. Затем повторяя операнд в середине дважды выполните побочный эффект. Опускание среднего операнда использует значение, уже вычисленное без нежелательные эффекты пересчета.
Как вы, наверное, можете догадаться, избегая этого, рекомендуется для удобства чтения и переносимости. Я искренне удивлен, увидев такое грамматически-несовместимое расширение C.
Ответ 2
Это расширение GCC, которое означает "если условие истинно, используйте его, иначе используйте другое значение", поэтому
machine->max_cpus = machine->max_cpus ?: 1;
является сокращением для
machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;
хотя если условие имеет побочные эффекты, оно будет выполняться только один раз
Ответ 3
Используя gcc -pedantic flag, он говорит
foo.c: 5: предупреждение: ISO C запрещает опустив средний член a: Выражение
Ответ 4
Это расширение GCC, и оно становится более интересным и полезным, когда условие имеет побочные эффекты.
В этом случае, да, я бы согласился, что это неясно больше всего.
Ответ 5
K & R BNF показывает, что требуется выражение между "?" а также ":". Я не думаю, что gcc должен компилировать это без диагностики.
Ответ 6
Для этого есть еще один полезный случай: устранение промежуточных переменных при вызове функции или метода, которые могут возвращать нуль, что мы хотим избежать вызова дважды. Например (Objective-C), предположим, что мы хотим распаковать файл в массив, если он существует, иначе вернем пустой массив.
- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
NSArray *backlog = @[];
NSData *backlogData = [NSData dataWithContentsOfFile:path];
if (backlogData)
{
backlog = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData] ?: backlog;
}
return backlog;
}
Альтернативы менее сжатые.
- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
NSArray *backlog = @[];
NSData *backlogData = [NSData dataWithContentsOfFile:path];
if (backlogData)
{
NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
if (tempArray != nil)
{
backlog = tempArray;
}
}
return backlog;
}
Или уродливее с несколькими возвратами и т.д.
- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
NSData *backlogData = [NSData dataWithContentsOfFile:path];
if (backlogData)
{
NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
if (tempArray != nil)
{
return tempArray;
}
}
return @[];
}
Так что это полезный синтаксический сахар, который я считаю достаточно читаемым. Недостатками являются
-
Неявное преобразование указателя в bool. Это давний C
конвенции, но большинство современных языков запрещают это, усложняя
любые усилия по переносу.
-
Как говорили другие, это также нестандартное расширение, поэтому оно должно
следует избегать, если переносимость является вообще соображением.