Приоритет и ассоциативность операторов

Когда выражение содержит несколько операторов, порядок вычисления отдельных операторов задается приоритетом операторов. Например, выражение x + y * z вычисляется как x + (y * z), поскольку оператор * имеет более высокий приоритет по сравнению с бинарным оператором +. Приоритет оператора задается в определении связанной с ним грамматической структуры. Например, аддитивное выражение состоит из последовательности мультипликативных выражений, разделенных операторами + или -, таким образом, операторы + или - имеют более низкий приоритет, чем операторы *, / и %.

В следующей таблице приводятся все операторы в соответствии с их приоритетом от самого высокого до самого низкого.

Раздел Категория Операторы
7.6 Основной x.y f(x) a[x] x++ x-- new typeof default checked unchecked delegate
7.7 Унарный + - ! ~ ++x --x (T)x
7.8 Мультипликативный * / %
7.8 Аддитивный + -
7.9 Сдвиг << >>
7.10 Отношение и проверка типа < > <= >= is as
7.10 Равенство == !=
7.11 Логическое И &
7.11 Исключающее ИЛИ ^
7.11 Логическое ИЛИ |
7.12 Условное И &&
7.12 Условное ИЛИ ||
7.13 Объединение с нулем ??
7.14 Условный ?:
7.17, 7.15 Присваивание и лямбда-выражение = *= /= %= += -= <<= >>= &= ^= |= =>

Когда операнд находится между двух операторов с одним приоритетом, порядок выполнения операций определяется ассоциативностью операторов.

· За исключением операторов присваивания и оператора слияния с NULL, все бинарные операторы обладают левой ассоциативностью. Это означает, что все операции выполняются слева направо. Например, выражение x + y + z вычисляется как (x + y) + z.

· Операторы присваивания, оператор слияния с NULL и условный оператор (?:) обладают правой ассоциативностью. Это означает, что все операции выполняются справа налево. Например, выражение x = y = z вычисляется как x = (y = z).

Приоритетом и ассоциативностью можно управлять с помощью скобок. Например, в выражении x + y * z сначала y умножается на z, а затем результат складывается с x, но в выражении (x + y) * z сначала складываются x и y, а затем результат умножается на z.

Перегрузка операторов

У всех унарных и бинарных операторов есть стандартная реализация, доступная автоматически в любом выражении. В дополнение к стандартным реализациям объявление operator в классах и структурах позволяет использовать пользовательские реализации (§10.10). Пользовательские реализации операторов всегда имеют приоритет над предопределенными реализациями операторов: только в случае, если не существует пользовательских реализаций операторов, будут рассматриваться предопределенные реализации операторов, как описано в §7.3.3 and §7.3.4.

К унарным операторам, допускающим перегрузку, относятся:

+ - ! ~ ++ -- true false

Несмотря на то что true и false явно в выражениях не используются (и поэтому не включены в таблицу с приоритетами в разделе §7.3.1), они считаются операторами, потому что вызываются в некоторых контекстах выражений: логические выражения (§7.20), выражения, включающие условия (§7.14), и логические условные операторы (§7.12).

К бинарным операторам, допускающим перегрузку, относятся:

+ - * / % & | ^ << >> == != > < >= <=

Перегрузка возможна только для операторов, указанных выше. В частности, невозможна перегрузка операторов доступа к членам, вызова методов или операторов =, &&, ||, ??, ?:, =>, checked, unchecked, new, typeof, default, as и is.

При перегрузке бинарного оператора связанный оператор присваивания (если он есть) также неявно перегружается. Например, при перегрузке оператора * также выполняется перегрузка оператора *=. Это описано далее в §7.17.2. Обратите внимание, что сам оператор присваивания (=) нельзя перегрузить. При присваивании всегда происходит простое побитовое копирование значения в переменную.

Перегрузка операций приведения типов, например (T)x, осуществляется путем предоставления пользовательских преобразований (§6.4).

Доступ к элементу, например a[x], не считается оператором, допускающим перегрузку. Вместо этого поддерживается пользовательская индексация в индексаторах (§10.9).

В выражениях ссылка на операторы осуществляется с помощью записи в виде оператора, а в объявлениях ссылка на операторы осуществляется с помощью записи в виде функции. В следующей таблице показано отношение между записью в виде оператора и функции для унарных и бинарных операторов. В первой строке op означает любой унарный оператор в виде префикса, допускающий перегрузку. Во второй строке op означает унарный оператор ++ или -- в виде постфикса. В третьей строке op означает любой бинарный оператор, допускающий перегрузку.



Запись в виде оператора Запись в виде функции
op x operator op(x)
x op operator op(x)
x op y operator op(x, y)

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

В объявлениях пользовательских операторов нельзя изменять синтаксис, приоритет и ассоциативность оператора. Например, оператор / всегда будет бинарным оператором, иметь приоритет, указанный в разделе §7.3.1 и всегда будет обладать левой ассоциативностью.

Хотя пользовательский оператор может выполнять любые вычисления, настоятельно не рекомендуется создавать реализации, результат которых не является интуитивно ожидаемым. Например, в реализации operator == должно производиться сравнение двух операндов на равенство и возвращаться соответствующий результат типа bool.

В описании отдельных операторов в разделах §7.6–§7.12 указаны стандартные реализации и дополнительные правила, применимые к каждому оператору. В этих описаниях используются термины разрешение перегрузки унарных операторов, разрешение перегрузки бинарных операторов и числовое расширение, определения которых находятся в дальнейших разделах.

Наши рекомендации