Зачем использовать троичный оператор, не назначая значение для «истинного» условия (x = x?: 1) - PullRequest
38 голосов
/ 11 мая 2010

В коде 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/.

Ответы [ 6 ]

48 голосов
/ 11 мая 2010

Это разрешено в GNU как неясное расширение C

5.7 Условные выражения с пропущенными операндами

Средний операнд в условном выражении Выражение может быть опущено. Тогда если первый операнд ненулевой, его значение значение условного выражение.

Следовательно, выражение

 x ? : y

имеет значение x, если оно отлично от нуля; в противном случае значение y.

Этот пример совершенно эквивалентен до

 x ? x : y

В этом простом случае способность опустить средний операнд не особенно полезно. Когда это станет полезно, когда первый операнд делает, или может (если это аргумент макроса), содержат побочный эффект. Затем повторяя операнд в середине будет выполните побочный эффект дважды. Опуская средний операнд использует значение уже вычислено без нежелательные последствия повторного вычисления.

Как вы, наверное, догадались, избегать этого рекомендуется из соображений удобства чтения и переносимости. Я искренне удивлен, увидев такое несовместимое с грамматикой расширение C.

10 голосов
/ 11 мая 2010

Это расширение GCC , что означает "если условие истинно, используйте его, иначе используйте это другое значение", поэтому

machine->max_cpus = machine->max_cpus ?: 1;

является сокращением для

machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;

хотя, если условие имеет побочные эффекты, оно будет запущено только один раз

6 голосов
/ 11 мая 2010

Используя флаг gcc -pedantic, он говорит:

foo.c: 5: предупреждение: ISO C запрещает опуская средний член? выражение

3 голосов
/ 11 мая 2010

Это расширение GCC , и оно становится более интересным и полезным, когда условие имеет побочные эффекты.

В этом случае, да, я бы согласился, что это неясно больше, чем что-либо еще.

1 голос
/ 11 мая 2010

K & R BNF показывает, что требуется выражение между "?" а также ":". Я не думаю, что gcc должен компилировать это без диагностики.

0 голосов
/ 22 апреля 2016

Для этого есть еще один полезный случай - исключение промежуточных переменных при вызове функции или метода, который может вернуть nil, который мы хотим избежать вызова дважды. Например, (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 условно, но большинство современных языков это запрещают, усложняя любые усилия по переносу.

  • Как уже говорили другие, это также нестандартное расширение, поэтому оно должно следует избегать, если переносимость является фактором вообще.

...