Другие операторы
| Название оператора | Синтаксис | Перегружаемый | Примеры прототипов (для classT) | |
|---|---|---|---|---|
| Внутри определения класса | Вне определения класса | |||
| вызов функции |
a(a1, a2) | Да |
R T::operator()(Arg1 &a1, Arg2 &a2, ...); | Н/Д |
| запятая |
a, b | Да |
T2& T::operator,(T2 &b); |
T2& operator,(const T &a, T2 &b); |
| условный оператор |
a ? b : c | Нет | Н/Д | Н/Д |
Оператор вызова функции предоставляет семантику функции для любого объекта.
Условный оператор (в просторечии называемый тернарным условным оператором ) проверяет логическое значение первого выражения и, в зависимости от полученного значения, вычисляет и возвращает второе или третье выражение.
Встроенный оператор вызова функции
Выражения вызова функции имеют следующую форму:
function (arg1, arg2, arg3,...) |
| function | - | выражение типа функции или типа указателя на функцию |
arg1, arg2, arg3,... | - | возможно, пустой список произвольных выражений или списков инициализации в фигурных скобках, ограниченных фигурными скобками(начиная с C++11), за исключением того, что оператор запятая не допускается на верхнем уровне во избежание неоднозначности |
При вызове нестатической функции или статической функции-члена function может быть lvalue, ссылающимся на функцию (в этом случае преобразование функции в указатель подавляется), либо prvalue типа указателя на функцию.
Имя функции (или члена), заданное посредством function, может быть перегружено; правила разрешения перегрузки используются для определения вызываемой перегрузки.
Если function задаёт функцию-член, она может быть виртуальной; в таком случае вызывается конечная переопределяющая функция с использованием динамической диспетчеризации во время выполнения.
Каждый параметр функции инициализируется соответствующим аргументом после выполнения неявного преобразования, если оно необходимо.
- Если соответствующего аргумента нет, используется соответствующий аргумент по умолчанию; если его также нет, программа некорректна.
- Если вызов производится для функции-члена, то указатель
thisна текущий объект преобразуется, как если бы применялось явное приведение к указателюthis, ожидаемому функцией. - Инициализация и уничтожение каждого параметра происходят в контексте полного выражения, в котором появляется вызов функции. Это означает, например, что если конструктор или деструктор параметра выбрасывает исключение, то блоки try функции, в которой он был вызван, не рассматриваются.
Если функция является функцией с переменным числом аргументов, ко всем аргументам, соответствующим параметру-многоточию, применяются стандартные повышения аргументов.
Поведение реализации определяет, уничтожается ли параметр при выходе из функции, в которой он определён, или в конце охватывающего полного выражения. Параметры всегда уничтожаются в порядке, обратном порядку их создания.
Тип выражения вызова функции — это тип возвращаемого значения выбранной функции, определяемый статическим связыванием (без учёта ключевого слова virtual), даже если фактически вызванная переопределяющая функция возвращает другой тип. Это позволяет переопределяющим функциям возвращать указатели или ссылки на классы, производные от класса типа, возвращаемого базовой функцией, то есть C++ поддерживает ковариантные типы возвращаемого значения. Если function задаёт деструктор, типом возвращаемого значения является void.
| Когда объект типа класса Временный объект конструируется соответственно из аргумента функции или возвращаемого значения, а параметр функции или объект результата инициализируется так, как если бы для копирования временного объекта использовался не удалённый тривиальный конструктор (даже если этот конструктор недоступен или не был бы выбран разрешением перегрузки для копирования или перемещения объекта). Это позволяет передавать в функции и возвращать из них в регистрах объекты небольших типов классов, например | (начиная с C++17) |
Категория значения выражения вызова функции — lvalue, если функция возвращает lvalue-ссылку или rvalue-ссылку на функцию; xvalue, если функция возвращает rvalue-ссылку на объект; и prvalue в остальных случаях. Если выражение вызова функции является prvalue объектного типа, оно должно иметь полный тип, за исключением случаев, когда оно используется в качестве операнда decltype (или в качестве правого операнда встроенного оператора запятая, который является операндом decltype)(начиная с C++11).
| При нормальном выходе из вызванной функции все постусловные утверждения функции вычисляются последовательно. Если реализация вводит какие-либо временные объекты для хранения значения результата, для вычисления
| (начиная с C++26) |
Синтаксис выражения вызова функции похож на синтаксис value initialization T(), выражения приведения в стиле функции T(A1) и прямой инициализации временного объекта T(A1, A2, A3, ...), где T — имя типа.
#include <cstdio>
struct S
{
int f1(double d)
{
return printf("%f \n", d); // variable argument function call
}
int f2()
{
return f1(7); // member function call, same as this->f1()
// integer argument converted to double
}
};
void f()
{
puts("function called"); // function call
}
int main()
{
f(); // function call
S s;
s.f2(); // member function call
}
Вывод:
function called 7.000000
Встроенный оператор запятая
Выражения с запятой имеют следующую форму:
E1 , E2 |
В выражении с запятой E1, E2 сначала вычисляется выражение E1, его результат отбрасывается (однако, если оно имеет тип класса, оно не уничтожается до конца содержащего его полного выражения), а его побочные эффекты завершаются до начала вычисления выражения E2 (обратите внимание, что пользовательский operator, не может гарантировать упорядочение)(до C++17).
Тип, значение и категория значения результата выражения с запятой в точности совпадают с типом, значением и категорией значения второго операнда, E2. Если E2 является временным выражением(начиная с C++17), результатом выражения является это временное выражение(начиная с C++17). Если E2 является битовым полем, результат также является битовым полем.
Запятая в различных разделённых запятыми списках, таких как списки аргументов функции (f(a, b, c)) и списки инициализации int a[] = {1, 2, 3}, не является оператором запятая. Если в таких контекстах необходимо использовать оператор запятая, его нужно заключить в скобки: f(a, (n++, n + b), c).
| Использование незаключённого в скобки выражения с запятой в качестве второго (правого) аргумента оператора индексирования устарело. Например, |
(начиная с C++20) (до C++23) |
| Незаключённое в скобки выражение с запятой не может быть вторым (правым) аргументом оператора индексирования. Например, При использовании выражения с запятой в качестве индекса необходимы скобки, например | (начиная с C++23) |
#include <iostream>
int main()
{
// comma is often used to execute more than one expression
// where the language grammar allows only one expression:
// * in the third component of the for loop
for (int i = 0, j = 10; i <= j; ++i, --j)
// ^list separator ^comma operator
std::cout << "i = " << i << " j = " << j << '\n';
// * in a return statement
// return log("an error!"), -1;
// * in an initializer expression
// MyClass(const Arg& arg)
// : member{ throws_if_bad(arg), arg }
// etc.
// comma operators can be chained; the result of the last
// (rightmost) expression is the result of the whole chain:
int n = 1;
int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n);
// m is now 6
std::cout << "m = " << (++m, m) << '\n';
}
Вывод:
i = 0 j = 10 i = 1 j = 9 i = 2 j = 8 i = 3 j = 7 i = 4 j = 6 i = 5 j = 5 n = 2 m = 7
Условный оператор
Выражения с условным оператором имеют вид
E1 ? E2 : E3 |
E1 вычисляется и контекстно преобразуется к типу bool; если результат равен true, результатом условного выражения является значение E2 ; в противном случае результатом условного выражения является значение E3 .
Тип и категория значения условного выражения E1 ? E2 : E3 определяются следующим образом:
Этап 1
Если E2 и E3 имеют тип void, результатом является rvalue(до C++11)prvalue(начиная с C++11) типа void.
Если ровно одно из выражений E2 и E3 имеет тип void:
- Если операнд типа void является (возможно, заключённым в скобки) выражением throw, результат имеет тип и категорию значения другого операнда[1]. Если другой операнд является битовым полем, результат также является битовым полем.
- В противном случае программа некорректна.
Если ни одно из выражений E2 и E3 не имеет тип void, переходят к следующему этапу.
2 + 2 == 4 ? throw 123 : throw 456; // the result is of type “void”
2 + 2 != 4 ? "OK" : throw "error"; // the result is of type “const char[3]”
// even if an exception is always thrown
Этап 2
Если E2 или E3 являются lvalue-битовыми полями(до C++11)glvalue-битовыми полями одной категории значения(начиная с C++11) типов cv1 T и cv2 T соответственно, то для дальнейших действий считается, что операнды имеют тип cv T, где cv представляет собой объединение cv1 и cv2.
Если E2 и E3 имеют разные типы и выполняется любое из следующих условий, переходят к этапу 3:
- По крайней мере одно из выражений
E2иE3имеет тип класса (возможно, с квалификаторами cv). - Оба выражения
E2иE3являются lvalue одного типа(до C++11)glvalue одной категории значения и одного типа(начиная с C++11), за исключением квалификаторов cv.
В противном случае переходят к этапу 4.
Этап 3
Предпринимаются попытки сформировать последовательность неявного преобразования[2] из выражения-операнда X типа TX в целевой тип, связанный с типом TY выражения-операнда Y, следующим образом:
- Если
Yявляется lvalue, целевым типом являетсяTY&, однако последовательность неявного преобразования может быть сформирована только в том случае, если ссылка могла бы связываться напрямую с lvalue(до C++11)glvalue(начиная с C++11).
| (начиная с C++11) |
- Если
Yявляется rvalue(до C++11)prvalue(начиная с C++11) или если ни одну из указанных выше последовательностей преобразования сформировать не удалось, а по крайней мере одно из выраженийTXиTYимеет тип класса (возможно, с квалификаторами cv):- Если
TXиTYявляются одним и тем же типом класса (без учёта квалификаторов cv):- Если
TYимеет не меньше квалификаторов cv, чемTX, целевым типом являетсяTY. - В противном случае последовательность преобразования не формируется.
- Если
- В противном случае, если
TYявляется базовым классомTX, целевым типом являетсяTYс квалификаторами cv типаTX. - В противном случае целевым типом является тип
Z, гдеZ— это значениеYпосле применения стандартных преобразований lvalue-в-rvalue, массива-в-указатель и функции-в-указатель.
- Если
- В противном случае последовательность преобразования не формируется.
С помощью этого процесса определяется, можно ли сформировать последовательность неявного преобразования из E2 в целевой тип, определённый для E3, и наоборот.
- Если последовательность преобразования сформировать нельзя, переходят к следующему этапу.
- Если можно сформировать ровно одну последовательность преобразования:
- Если последовательность преобразования неоднозначна, программа некорректна.
- В противном случае это преобразование применяется к выбранному операнду, и преобразованный операнд используется вместо исходного в дальнейших действиях; затем переходят к следующему этапу.
- Если можно сформировать обе последовательности, программа некорректна.
struct A {};
struct B : A {};
using T = const B;
A a = true ? A() : T(); // Y = A(), TY = A, X = T(), TX = const B, Target = const A
Этап 4
| Если | (до C++11) |
| Если | (начиная с C++11) |
В противном случае результатом является rvalue(до C++11)prvalue(начиная с C++11).
- Если
E2иE3имеют разные типы и хотя бы один из них имеет тип класса (возможно, с квалификаторами cv), переходят к этапу 5. - В противном случае переходят к этапу 6.
Этап 5
Выполняется разрешение перегрузки с использованием встроенных кандидатов для попытки преобразовать операнды к встроенным типам:
- Если разрешение перегрузки завершается неудачей, программа некорректна.
- В противном случае выбранные преобразования применяются, а преобразованные операнды используются вместо исходных в дальнейших действиях. Переходят к следующему этапу.
Этап 6
К (возможно, преобразованным) выражениям E2 и E3 применяются преобразования массива-в-указатель и функции-в-указатель. После этих преобразований должно выполняться хотя бы одно из следующих условий, иначе программа некорректна:
-
E2иE3имеют один и тот же тип. В этом случае результат имеет этот тип и копирующе инициализируется выбранным операндом. - Оба выражения
E2иE3имеют арифметический тип или тип перечисления. В этом случае к ним применяются обычные арифметические преобразования, чтобы привести их к общему типу; результат имеет этот тип. - По крайней мере один из операндов
E2иE3является указателем. В этом случае применяются преобразования lvalue-в-rvalue, указатель, указатель на функцию(начиная с C++17) и преобразования квалификации, чтобы привести их к составному типу указателя; результат имеет этот тип. - По крайней мере один из операндов
E2иE3является указателем на член. В этом случае применяются преобразования lvalue-в-rvalue, указатель-на-член, указатель на функцию(начиная с C++17) и преобразования квалификации, чтобы привести их к составному типу указателя; результат имеет этот тип.
| (начиная с C++11) |
int* intPtr;
using Mixed = decltype(true ? nullptr : intPtr);
static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*
struct A
{
int* m_ptr;
} a;
int* A::* memPtr = &A::m_ptr; // memPtr is a pointer to member m_ptr of A
// memPtr makes nullptr as type of pointer to member m_ptr of A
static_assert(std::is_same_v<decltype(false ? memPtr : nullptr), int*A::*>);
// a.*memPtr is now just pointer to int and nullptr also becomes pointer to int
static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
- Такой условный оператор часто использовался в C++11 для программирования с constexpr до появления C++14.
- Доступ к члену, а также сведения о том, удалена ли функция преобразования(начиная с C++11) и является ли операнд битовым полем, не учитываются.
| Тип результата условного оператора также доступен как бинарный признак типа | (начиная с C++11) |
Перегрузки
Для каждой пары повышенных арифметических типов L и R и для каждого типа P, где P является указателем, указателем на член или перечислением с областью видимости, в разрешении перегрузки участвуют следующие сигнатуры функций:
LR operator?:(bool, L, R); | ||
P operator?:(bool, P, P); |
где LR — результат применения обычных арифметических преобразований к L и R.
Оператор «?:» нельзя перегрузить; эти сигнатуры функций существуют только для целей разрешения перегрузки.
#include <iostream>
#include <string>
struct Node
{
Node* next;
int data;
// deep-copying copy constructor
Node(const Node& other)
: next(other.next ? new Node(*other.next) : NULL)
, data(other.data)
{}
Node(int d) : next(NULL), data(d) {}
~Node() { delete next; }
};
int main()
{
// simple rvalue example
int n = 1 > 2 ? 10 : 11; // 1 > 2 is false, so n = 11
// simple lvalue example
int m = 10;
(n == m ? n : m) = 7; // n == m is false, so m = 7
//output the result
std::cout << "n = " << n << "\nm = " << m;
}
Вывод:
n = 11 m = 7
Стандартная библиотека
Многие классы стандартной библиотеки перегружают operator() для использования в качестве функциональных объектов.
| удаляет объект или массив (открытая функция-член std::default_delete<T>) |
|
| возвращает сумму двух аргументов (открытая функция-член std::plus<T>) |
|
| возвращает разность двух аргументов (открытая функция-член std::minus<T>) |
|
| возвращает произведение двух аргументов (открытая функция-член std::multiplies<T>) |
|
| возвращает результат деления первого аргумента на второй (открытая функция-член std::divides<T>) |
|
| возвращает остаток от деления первого аргумента на второй (открытая функция-член std::modulus<T>) |
|
| возвращает отрицание аргумента (открытая функция-член std::negate<T>) |
|
| проверяет, равны ли аргументы (открытая функция-член std::equal_to<T>) |
|
| проверяет, не равны ли аргументы (открытая функция-член std::not_equal_to<T>) |
|
| проверяет, больше ли первый аргумент второго (открытая функция-член std::greater<T>) |
|
| проверяет, меньше ли первый аргумент второго (открытая функция-член std::less<T>) |
|
| проверяет, больше ли первый аргумент второго или равен ему (открытая функция-член std::greater_equal<T>) |
|
| проверяет, меньше ли первый аргумент второго или равен ему (открытая функция-член std::less_equal<T>) |
|
| возвращает результат логической операции И над двумя аргументами (открытая функция-член std::logical_and<T>) |
|
| возвращает результат логической операции ИЛИ над двумя аргументами (открытая функция-член std::logical_or<T>) |
|
| возвращает результат логической операции НЕ над аргументом (открытая функция-член std::logical_not<T>) |
|
| возвращает результат побитовой операции И над двумя аргументами (открытая функция-член std::bit_and<T>) |
|
| возвращает результат побитовой операции ИЛИ над двумя аргументами (открытая функция-член std::bit_or<T>) |
|
| возвращает результат побитовой операции исключающего ИЛИ над двумя аргументами (открытая функция-член std::bit_xor<T>) |
|
| возвращает логическое дополнение результата вызова сохранённого предиката (открытая функция-член std::unary_negate<Predicate>) |
|
| возвращает логическое дополнение результата вызова сохранённого предиката (открытая функция-член std::binary_negate<Predicate>) |
|
| вызывает сохранённую функцию (открытая функция-член std::reference_wrapper<T>) |
|
| вызывает целевой объект (открытая функция-член std::function<R(Args...)>) |
|
| вызывает целевой объект (открытая функция-член std::move_only_function) |
|
| вызывает целевой объект (открытая функция-член std::copyable_function) |
|
| возобновляет выполнение сопрограммы (открытая функция-член std::coroutine_handle<Promise>) |
|
| сравнивает две строки в лексикографическом порядке с помощью фасета collate данной локали (открытая функция-член std::locale) |
|
сравнивает два значения типа value_type (открытая функция-член std::map<Key,T,Compare,Allocator>::value_compare) |
|
сравнивает два значения типа value_type (открытая функция-член std::multimap<Key,T,Compare,Allocator>::value_compare) |
|
| выполняет функцию (открытая функция-член std::packaged_task<R(Args...)>) |
|
| продвигает состояние генератора и возвращает сгенерированное значение (открытая функция-член std::linear_congruential_engine<UIntType,a,c,m>) |
|
|
(C++11) | генерирует следующее случайное число из распределения (открытая функция-член std::uniform_int_distribution<IntType>) |
Оператор запятая не перегружен ни одним классом стандартной библиотеки. Библиотека boost использует operator, в boost.assign, boost.spirit и других библиотеках. Библиотека доступа к базам данных SOCI также перегружает operator,.
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применён к | Поведение согласно опубликованному стандарту | Корректное поведение |
|---|---|---|---|
| CWG 446 | C++98 | не было определено, создаётся ли временный объект при преобразовании lvalue в rvalue для условного оператора | временный объект всегда создаётся, если оператор возвращает rvalue класса |
| CWG 462 | C++98 | если второй операнд оператора запятая является временным объектом, не было определено, продлевается ли время его жизни, когда результат выражения с оператором запятая привязывается к ссылке | в этом случае результат выражения с оператором запятая является временным объектом (поэтому время его жизни продлевается) |
| CWG 587 | C++98 | если второй и третий операнды условного оператора являются lvalue одного типа, различающимися только cv-квалификацией, результатом было lvalue, если эти операнды имели классовые типы, и rvalue в противном случае | в этом случае результатом всегда является lvalue |
| CWG 1029 | C++98 | тип вызова деструктора не был определён | определён как void |
| CWG 1550 | C++98 | выражения throw в скобках не допускались в условных выражениях, если другой операнд не имел тип void | допускаются |
| CWG 1560 | C++98 | операнд типа void условного оператора вызывал необоснованное преобразование lvalue в rvalue для другого операнда, всегда приводя к rvalue | условное выражение с void может быть lvalue |
| CWG 1642 | C++98 | выражение function в выражении вызова функции могло быть lvalue-указателем на функцию | не допускается |
| CWG 1805 | C++98 | при определении целевого типа для последовательности неявного преобразования не был ясен способ преобразования Y в Z | прояснено |
| CWG 1895 | C++98 C++11 | не было ясно, препятствует ли преобразованию в условных выражениях удалённая (C++11) или недоступная (C++98) функция преобразования; также не рассматривались преобразования из базового класса в prvalue производного класса | обрабатывается так же, как разрешение перегрузки |
| CWG 1932 | C++98 | поля битов одного типа не учитывались в условных выражениях | обрабатываются с учётом базовых типов |
| CWG 2226 | C++11 | при определении целевого типа другого операнда условного оператора ссылка не могла привязываться к xvalue, если этот операнд является lvalue | разрешено |
| CWG 2283 | C++17 | требование полноты типа для оператора вызова функции было случайно удалено документом P0135R1 | требование восстановлено |
| CWG 2321 | C++98 | при определении целевого типа другого операнда условного оператора тип производного класса нельзя было преобразовать в тип базового класса с меньшим числом cv-квалификаторов | разрешено преобразование к типу базового класса с cv-квалификацией операнда производного класса |
| CWG 2715 | C++98 | инициализация и уничтожение каждого параметра происходили бы в контексте вызывающей функции, которой может не существовать[1] | происходит в контексте охватывающего полного выражения |
| CWG 2850 | C++98 | порядок уничтожения параметров был неясен | прояснён |
| CWG 2865 | C++98 | если TX и TY имеют один и тот же тип класса, а TXимеет более строгую cv-квалификацию, чем TY, последовательность неявного преобразованиявсё ещё могла быть сформирована из prvalue Y | в этом случае последовательность преобразования не формируется |
| CWG 2906 | C++98 | преобразования lvalue в rvalue безусловно применялись в случае результата rvalue условного оператора | применяются только в некоторых случаях |
- Например, функции можно вызывать в инициализаторе переменной в области пространства имён; в этом контексте «вызывающей функции» нет.
См. также
Приоритет операторов
Перегрузка операторов
| Распространённые операторы | ||||||
|---|---|---|---|---|---|---|
| Присваивание |
Инкремент, декремент | Арифметические | Логические | Сравнения |
Доступ к членам | Другие |
a = b a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b | ++a --a a++ a-- | +a -a a + b a - b a * b a / b a % b ~a a & b a | b a ^ b a << b a >> b | !a a && b a || b | a == b a != b a < b a > b a <= b a >= b a <=> b | a[...] *a &a a->b a.b a->*b a.*b | вызов функцииa(...) |
запятаяa, b |
||||||
условный операторa ? b : c |
||||||
| Специальные операторы | ||||||
|
|
||||||
| Документация по C для Другие операторы |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/operator_other