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