Другие операторы
| Имя оператора | Синтаксис | Перегружаемый | Примеры прототипов (для class T) | |
|---|---|---|---|---|
| Внутри определения класса | Вне определения класса | |||
| вызов функции |
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 | Нет | Не применимо | Не применимо |
Объяснение
Оператор вызова функции предоставляет семантику функции для любого объекта.
Условный оператор (в разговорной речи — тройной условный) проверяет булево значение первого выражения и, в зависимости от результата, вычисляет и возвращает либо второе, либо третье выражение.
Встроенный оператор вызова функции
Выражения вызова функции имеют вид
функция (арг1, арг2, арг3,...) |
где
- функция — выражение типа функции или указателя на функцию, и
- арг1
,арг2,арг3,... — возможно пустой список произвольных выраженийили списки инициализации в фигурных скобках(с C++11), за исключением оператора запятой на верхнем уровне, чтобы избежать неоднозначности.
Для вызова нестатического члена функции или статического члена функции, функция может быть lvalue, ссылающимся на функцию (в этом случае преобразование функции в указатель на функцию подавляется), или prvalue типа указателя на функцию.
Имя функции (или члена), указанное функция, может быть перегружено, правила разрешения перегрузки используются для определения, какой перегрузке следует вызвать.
Если функция указывает на член-функцию, она может быть виртуальной, в этом случае будет вызван окончательный переопределитель этой функции, используя динамическую диспетчеризацию во время выполнения.
Для вызова функции,
| Выражение функция, а также все выражения арг1, арг2, арг3 и т. д., предоставленные в качестве аргументов, вычисляются в произвольном порядке, неопределённом по отношению друг к другу. | (до C++17) |
| функция выполняется перед каждым из арг1, арг2, арг3, а также стандартными аргументами, если таковые имеются. Выражения аргументов вычисляются в произвольном порядке, неопределённо по отношению друг к другу. | (с C++17) |
Каждый параметр функции инициализируется соответствующим аргументом после неявного преобразования, если это необходимо.
- Если соответствующего аргумента нет, используется соответствующий стандартный аргумент, а если такового нет, программа некорректна.
- Если вызов производится для члена-функции, то указатель на текущий объект преобразуется так, как если бы он был явным приведением к типу указателя, ожидаемого функцией.
- Инициализация и уничтожение каждого параметра происходит в контексте полного выражения, где появляется вызов функции, что означает, например, что если конструктор или деструктор параметра вызывает исключение, блоки try вызываемой функции не рассматриваются.
Если функция является функцией с переменным числом аргументов, преобразования по умолчанию применяются ко всем аргументам, соответствующим параметру эллипса. Определяется ли срок жизни параметра по завершении функции, в которой он определен, или по завершении содержащего его выражения — зависит от реализации.
Тип возвращаемого значения выражения вызова функции — это тип возвращаемого значения выбранной функции, определённый с помощью статического связывания (игнорируя ключевое слово virtual), даже если переопределяющая функция, которая фактически вызывается, возвращает другой тип. Это позволяет переопределяющим функциям возвращать указатели или ссылки на классы, которые производятся от типа возвращаемого значения базовой функции, т. е. C++ поддерживает ковариантные типы возвращаемых значений. Если функция указывает на деструктор, тип возвращаемого значения — void.
| Когда объект класса типа Временный объект создаётся из аргумента функции или значения возврата соответственно, и параметр или объект возврата функции инициализируются так, как будто с помощью тривиального конструктора копирования скопирован временный объект (даже если этот конструктор недоступен или не был бы выбран разрешением перегрузки для копирования или перемещения объекта). Это позволяет объектам небольших типов класса, таких как | (с C++17) |
Категория значения выражения вызова функции — lvalue, если функция возвращает ссылку на lvalue или ссылку на rvalue на функцию; xvalue, если функция возвращает ссылку на rvalue на объект; и prvalue в противном случае. Если выражение вызова функции является prvalue типа объекта, оно должно иметь полный тип, за исключением случаев, когда prvalue не материализуется, таких как(с C++17) использование его в качестве операнда decltype (или в качестве правого операнда встроенного оператора запятой, который является операндом decltype).
Выражение вызова функции аналогично по синтаксису инициализации значения 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 |
Первый операнд оператора условия оценивается и контекстуально преобразуется к типу bool. После завершения оценки значения и всех побочных эффектов первого операнда, если результат был true, вычисляется второй операнд. Если результат был false, вычисляется третий операнд.
Тип и категория значения условного выражения E1 ? E2 : E3 определяются по следующим правилам:
E2 или E3 тип void, то одно из следующих утверждений должно быть истинным, иначе программа некорректна:E2 либо E3 (но не оба) является (возможно, со скобками) выражением throw. Результат оператора условия имеет тип и категорию значения другого выражения. Если другое выражение является полем-битом, результат также является полем-битом. Такой оператор условия часто использовался в C++11 constexpr программировании до C++14. std::string str = 2 + 2 == 4 ? "OK" : throw std::logic_error("2 + 2 != 4");E2 и E3 имеют тип void (включая случай, когда оба являются выражениями throw). Результат является выражением prvalue типа void. 2 + 2 == 4 ? throw 123 : throw 456;
E2 или E3 являются glvalue-полями-битами одной категории значения и типов cv1 T и cv2 T соответственно, операнды рассматриваются как имеющие тип cv T для остальной части этого раздела, где cv — объединение cv1 и cv2.E2 и E3 имеют разные типы, по крайней мере один из которых является (возможно, с квалификаторами cv) типом класса, или оба являются glvalue той же категории значения и имеют тот же тип, за исключением квалификаторов cv, тогда делается попытка сформировать явную цепочку преобразований, игнорируя доступ к членам, является ли операнд полем-битом, или удалена ли функция преобразования для каждого из операндов до целевого типа, определяемого другим операндом(с C++11), как описано ниже. Операнд (назовем его X) типа TX может быть преобразован к целевому типу другого операнда (назовем его Y) типа TY следующим образом:Y является lvalue, целевой тип — TY&, и ссылка должна непосредственно связываться с lvalue(до C++11)glvalue(с C++11). 3.2) Если Y является xvalue, целевой тип — TY&&, и ссылка должна связываться непосредственно. | (с C++11) |
Y является rvalue(до C++11)prvalue(с C++11), или если ни одна из вышеперечисленных цепочек преобразований не может быть сформирована, и по крайней мере один из TX и TY является (возможно, с cv-квалификаторами) типом класса,TX и TY — один и тот же тип класса (без учёта cv-квалификаторов) и TY имеет не меньшие cv-квалификаторы, чем TX, целевой тип — TY,TY — базовый класс TX, целевой тип — TY с cv-квалификаторами TX: 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 AY получил бы после применения стандартных преобразований lvalue-to-rvalue, array-to-pointer и function-to-pointer явных преобразований.E2 в целевой тип E3 и E3 в целевой тип E2), или только одна может быть сформирована, но она является неоднозначной цепочкой преобразований, программа некорректна.E2 и E3 являются glvalue одного типа и одной категории значения, то результат имеет тот же тип и категорию значения, и является полем-битом, если по крайней мере один из E2 и E3 является полем-битом.E2 и E3 не имеют одного типа, и любой из них имеет (возможно, с квалификаторами cv) тип класса, выполняется разрешение перегрузки с использованием встроенных кандидатов ниже для попытки преобразования операндов к встроенным типам. Если разрешение перегрузки не удаётся, программа некорректна. В противном случае, выбранные преобразования применяются, и преобразованные операнды используются вместо исходных операндов для шага 6.E2 и E3 теперь имеют один тип, результат является выражением prvalue этого типа представляющим временный объект(до C++17)который инициализируется(с C++17) по копированию из операнда, который был выбран после оценки E1.E2 и E3 имеют арифметический или перечислительный тип: применяются обычные арифметические преобразования для приведения их к общему типу, и результат имеет этот тип.E2 и E3 — указатели, или один — указатель, а другой — константа нулевого указателя, применяются преобразования указателей и преобразования квалификаторов для приведения их к составному типу указателя, и результат имеет этот тип. int* intPtr; using Mixed = decltype(true ? nullptr : intPtr); static_assert(std::is_same_v<Mixed, int*>); // nullptr becoming int*
E2 и E3 — указатели на члены, или один — указатель на член, а другой — константа нулевого указателя, применяются преобразования указателей на члены и преобразования квалификаторов для приведения их к составному типу указателя, и результат имеет этот тип. 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*>);E2 и E3 — константы нулевого указателя, и по крайней мере один из них имеет тип std::nullptr_t, то тип результата — std::nullptr_t.Для каждой пары повышенных арифметических типов L и R и для каждого типа P, где P — тип указателя, указателя на член или перечислительный тип с областью видимости, следующие сигнатуры функций участвуют в разрешении перегрузки, выполняемом на шаге 5 правил выше:
LR operator?:(bool, L, R); | ||
P operator?:(bool, P, P); |
где LR — результат обычных арифметических преобразований, выполненных над L и R. Оператор «?:» перегрузить нельзя; эти сигнатуры функций существуют только для целей разрешения перегрузки.
Тип возвращаемого значения условного оператора также доступен как бинарный тип-свойство std::common_type.
#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::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...)>) |
|
|
(C++11) | переводит состояние генератора и возвращает сгенерированное значение (публичный член-функция 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 | в случае 3.3.3) условных выражений преобразования массива в указатель и функции в указатель не применялись | применить эти преобразования |
| CWG 1895 | C++98 C++11 | неясно, предотвращает ли удалённое (C++11) или недоступное (C++98) преобразование функция преобразования преобразование в условных выражениях, и преобразования из базового класса в производный класс prvalue не рассматривались | обрабатывается как разрешение перегрузки |
| CWG 1932 | C++98 | битовые поля одинакового типа отсутствовали в условных выражениях | обрабатываются базовыми типами |
| CWG 2226 | C++11 | при определении целевого типа другого операнда условного оператора, ссылка не могла привязаться к xvalue, если этот операнд является lvalue | допускается |
| CWG 2321 | C++98 | при определении целевого типа другого операнда условного оператора, тип производного класса не мог быть преобразован в базовый класс с меньшей cv-квалификацией | разрешено преобразовывать в базовый тип с cv-квалификацией от производного операнда |
| CWG 2715 | C++98 | инициализация и уничтожение каждого параметра происходили в контексте вызывающей функции, которая может отсутствовать[1] | происходит в контексте внешнего полного выражения |
- Например, функции могут вызываться в инициализаторе переменной на уровне пространства имён, в этом контексте нет «вызывающей функции».
См. также
Порядок выполнения операторов Перегрузка операторов
| Общие операторы | ||||||
|---|---|---|---|---|---|---|
| присваивание |
инкремент декремент | арифметические | логические | сравнения |
доступ к членам | прочие |
|
|
|
|
|
|
| вызов функции |
a(...) |
||||||
| запятая | ||||||
a, b |
||||||
| условный | ||||||
a ? b : c |
||||||
| Специальные операторы | ||||||
|
|
||||||
| C документация для Другие операторы |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/operator_other