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

Как определяется тип возвращаемого значения троичного оператора?

Я решал задачу о слоне на шахматной доске. В одном месте моего кода я включил следующую строку:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

Это приводит к следующей ошибке:

error: invalid operands of types 'int' and '<unresolved overloaded function type>' to binary 'operator<<'

Однако я мгновенно исправил эту ошибку, добавив в свой код дополнительную переменную:

int steps = (abs(c2-c1) == abs(r2-r1)) ? 1 : 2;
std::cout << steps << std::endl;

Как работает троичный оператор и как определяется его тип возвращаемого значения (как его назвал компилятор <unresolved overloaded function type>)?

4b9b3361

Ответ 1

Это не имеет ничего общего с тем, как выводится тип возвращаемого значения, и не связано с приоритетом оператора. Когда у вас есть

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

это не

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

потому что ?: имеет более низкий приоритет, чем <<. Это означает, что у вас есть

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

и именно поэтому вы получаете сообщение об ошибке <unresolved overloaded function type>. Просто используйте скобки, такие как

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

и ты будешь в порядке.

Ответ 2

Вы должны поставить круглые скобки вокруг троичной операции:

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

Если нет, оператор << переходит к 2 и выдает ошибку, потому что у него нет такой перегруженной функции.

Это происходит потому, что оператор побитового сдвига влево (<<) имеет более высокий приоритет, чем троичный оператор. Вы можете увидеть полный список операторов и их приоритет на этой странице ссылки C++.

Ответ 3

Из-за приоритета оператора эта строка обрабатывается как:

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

Измените его на

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;
//           ^----------------------------------^
//           Surrounding parentheses

Ответ 4

Легко увидеть ошибку, когда визуализируется порядок разбора:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;
\_______/                                                      <--- #1
             \________________________/   V   \~~~~error~~~/   <--- #2
             \_____________________________________________/   <--- #3
\__________________________________________________________/   <--- #4
\___________________________________________________________/  <--- #5

Ответ 5

Дословный ответ на заданный вами вопрос - алгоритм в разделе [expr.cond] стандарта языка C++.

Основное правило "определяло, может ли неявная последовательность преобразования быть сформирована из второго операнда в целевой тип, определенный для третьего операнда, и наоборот". Если нет одного возможного преобразования или имеется более одного, это синтаксическая ошибка, но есть несколько особых случаев (которые здесь не применяются):

  • Если оба являются арифметическими или enum типами, вы получаете один и тот же тип неявного преобразования для p ? a : b, который определяет тип выражения, например a + b или a * b.
  • Одна из целей может быть выражением throw и рассматривается как имеющая тип другой.
  • Если одна из целей является битовым полем, то тип условного выражения
  • Указатели с разными классификаторами (такими как const и volatile) объединяют свои классификаторы.

Результатом является glvalue, если цели являются glvalue того же типа, и prvalue в противном случае.

В случае сомнений вы всегда можете явно привести один или оба операнда так, чтобы они имели одинаковый тип.

Ваша настоящая проблема здесь - приоритет оператора, как объясняет принятый ответ. То есть компилятор анализирует третий операнд как 2 << std::endl, а не как 2.

Ответ 6

Согласно cppreference:

При синтаксическом анализе выражения оператор, который указан в некоторой строке таблицы выше с приоритетом, будет более тесно связан (как в скобках) со своими аргументами, чем любой оператор, который указан в строке ниже, с более низким приоритетом. Например, выражения std::cout << a & b и *p++ анализируются как (std::cout << a) & b и *(p++), а не как std::cout << (a & b) или (*p)++.

Операторы, имеющие одинаковый приоритет, связаны со своими аргументами в направлении их ассоциативности. Например, выражение a = b = c анализируется как a = (b = c), а не как (a = b) = c из-за ассоциативности назначения справа налево, но a + b - c анализируется (a + b) - c, а не a + (b - c) из-за левого правая ассоциативность сложения и вычитания.

Спецификация ассоциативности избыточна для унарных операторов и показана только для полноты: унарные префиксные операторы всегда ассоциируются справа налево (delete ++*p - это delete (++(*p))), а унарные постфиксные операторы всегда ассоциируются слева направо (a[1][2]++ - ((a[1])[2])++ ]). Обратите внимание, что ассоциативность имеет смысл для операторов доступа к элементам, даже если они сгруппированы с унарными постфиксными операторами: a.b++ анализируется (a.b)++, а не a.(b++).

Приоритет оператора не зависит от перегрузки оператора. Например, std::cout << a ? b : c; анализируется как (std::cout << a) ? b : c;, потому что приоритет арифметического сдвига влево выше, чем условный оператор.