Namespaces
Variants

Other operators

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Оператор
название
Синтаксис Перегружаемый Примеры прототипов (для 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 , ... )
function - тип функционального выражения или тип указателя на функцию
arg1 , arg2 , arg3 , ... - возможно пустой список произвольных выражений или списков инициализации в фигурных скобках (начиная с C++11) , за исключением того, что оператор запятой не допускается на верхнем уровне во избежание неоднозначности

Для вызова нечленной функции или статической функции-члена , function может быть lvalue, ссылающимся на функцию (в этом случае преобразование функции в указатель подавляется), или prvalue типа указателя на функцию.

Имя функции (или члена), указанное в function , может быть перегружено, разрешение перегрузки используется для определения, какая перегрузка должна быть вызвана.

Если function указывает на функцию-член, она может быть виртуальной, и в этом случае будет вызван конечный переопределитель этой функции с использованием динамического связывания во время выполнения.

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

  • Если соответствующий аргумент отсутствует, используется соответствующий аргумент по умолчанию , а если его нет, программа является некорректной.
  • Если вызов осуществляется для функции-члена, то указатель this на текущий объект преобразуется как при явном приведении к указателю this , ожидаемому функцией.
  • Инициализация и уничтожение каждого параметра происходит в контексте полного выражения , в котором появляется вызов функции, что означает, например, что если конструктор или деструктор параметра выбрасывает исключение, блоки try функции вызываемой функции не учитываются.

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

Реализация определяет, уничтожается ли параметр при выходе из функции, в которой он определен, или в конце объемлющего полного выражения. Параметры всегда уничтожаются в порядке, обратном порядку их создания.

Тип возвращаемого значения выражения вызова функции — это тип возвращаемого значения выбранной функции, определяемый с использованием статического связывания (игнорируя ключевое слово virtual ), даже если переопределяющая функция, которая фактически вызывается, возвращает другой тип. Это позволяет переопределяющим функциям возвращать указатели или ссылки на классы, производные от типа возврата базовой функции, т.е. C++ поддерживает ковариантные типы возврата . Если function определяет деструктор, тип возврата — void .

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

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

Это позволяет объектам небольших классовых типов, таким как std::complex или std::span , передаваться в функции или возвращаться из функций в регистрах.

(since C++17)

Категория значения выражения вызова функции является lvalue, если функция возвращает lvalue-ссылку или rvalue-ссылку на функцию, является xvalue, если функция возвращает rvalue-ссылку на объект, и является prvalue в остальных случаях. Если выражение вызова функции является prvalue объектного типа, оно должно иметь полный тип за исключением случаев использования в качестве операнда decltype (или в качестве правого операнда встроенного оператора запятой , который является операндом decltype ) (начиная с C++11) .

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

(since C++26)

Выражение вызова функции синтаксически схоже с инициализацией значения T ( ) , с функциональным приведением типа выражением T ( A1 ) , и с прямой инициализацией временного объекта T ( A1, A2, A3, ... ) , где T является именем типа.

#include <cstdio>
struct S
{
    int f1(double d)
    {
        return printf("%f \n", d); // вызов функции с переменным числом аргументов
    }
    int f2()
    {
        return f1(7); // вызов функции-члена, эквивалентно this->f1()
                      // целочисленный аргумент преобразован в double
    }
};
void f()
{
    puts("function called"); // вызов функции
}
int main()
{
    f();    // вызов функции
    S s;
    s.f2(); // вызов функции-члена
}

Вывод:

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 ) .

Использование не заключенного в скобки выражения с запятой в качестве второго (правого) аргумента оператора индексации устарело.

Например, a [ b, c ] устарело, а a [ ( b, c ) ] - нет.

(since C++20)
(until C++23)

Не заключенное в скобки выражение с запятой не может быть вторым (правым) аргументом оператора индексации . Например, a [ b, c ] является либо некорректным, либо эквивалентным a. operator [ ] ( b, c ) .

Скобки необходимы при использовании выражения с запятой в качестве индекса, например, a [ ( b, c ) ] .

(since C++23)
#include <iostream>
int main()
{
    // запятая часто используется для выполнения более одного выражения
    // там, где грамматика языка позволяет только одно выражение:
    // * в третьем компоненте цикла for
    for (int i = 0, j = 10; i <= j; ++i, --j)
    //            ^разделитель списка      ^оператор запятая
        std::cout << "i = " << i << " j = " << j << '\n';
    // * в операторе return
    // return log("an error!"), -1;
    // * в выражении инициализации
    // MyClass(const Arg& arg)
    // : member{ throws_if_bad(arg), arg }
    // и т.д.
    // операторы запятая могут быть объединены в цепочку; результат последнего
    // (самого правого) выражения является результатом всей цепочки:
    int n = 1;
    int m = (++n, std::cout << "n = " << n << '\n', ++n, 2 * n);
    // m теперь равно 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; // результат имеет тип “void”
2 + 2 != 4 ? "OK" : throw "error";  // результат имеет тип “const char[3]”
                                    // даже если исключение всегда выбрасывается

Этап 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) .
  • Если Y является xvalue, целевой тип — TY&& , но неявная последовательность преобразования может быть сформирована только если ссылка будет связываться напрямую.
(since 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-to-rvalue, array-to-pointer и function-to-pointer.
  • В противном случае последовательность преобразований не формируется.

С помощью этого процесса определяется, может ли быть сформирована неявная последовательность преобразования из 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, Цель = const A

Этап 4

Если E2 и E3 являются lvalues одного типа, то результат является lvalue того же типа и является битовым полем, если хотя бы один из E2 и E3 является битовым полем.

(до C++11)

Если E2 и E3 являются glvalues одного типа и одной категории значения, то результат имеет тот же тип и категорию значения, и является битовым полем, если хотя бы один из E2 и E3 является битовым полем.

(начиная с C++11)

В противном случае, результат является rvalue (до C++11) prvalue (начиная с C++11) .

  • Если E2 и E3 имеют разные типы, и хотя бы один из них имеет (возможно, cv-квалифицированный) классовый тип, переходите к этапу 5.
  • В противном случае переходите к этапу 6.

Этап 5

Разрешение перегрузки выполняется с использованием встроенных кандидатов для попытки преобразования операндов во встроенные типы:

  • Если разрешение перегрузки завершается неудачей, программа является некорректной.
  • В противном случае выбранные преобразования применяются, и преобразованные операнды используются вместо исходных операндов для последующей обработки. Переходите к следующему этапу.

Этап 6

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

  • E2 и E3 имеют одинаковый тип. В этом случае результат имеет этот тип и инициализируется копирующей инициализацией с использованием выбранного операнда.
  • Оба E2 и E3 имеют арифметический тип или тип перечисления. В этом случае применяются обычные арифметические преобразования для приведения их к общему типу, и результат имеет этот тип.
  • По крайней мере один из E2 и E3 является указателем. В этом случае применяются преобразования lvalue-to-rvalue, указателей , указателей на функции (since C++17) и квалификационные преобразования для приведения их к составному типу указателя , и результат имеет этот тип.
  • По крайней мере один из E2 и E3 является указателем на член. В этом случае применяются преобразования lvalue-to-rvalue, указателей на члены , указателей на функции (since C++17) и квалификационные преобразования для приведения их к составному типу указателя , и результат имеет этот тип.
  • Оба E2 и E3 являются константами нулевого указателя, и по крайней мере одна из них имеет тип std::nullptr_t . В этом случае результат имеет тип std::nullptr_t .
(since C++11)
int* intPtr;
using Mixed = decltype(true ? nullptr : intPtr);
static_assert(std::is_same_v<Mixed, int*>); // nullptr становится int*
struct A
{
    int* m_ptr;
} a;
int* A::* memPtr = &A::m_ptr; // memPtr - указатель на член m_ptr структуры A
// memPtr делает nullptr типом указателя на член m_ptr структуры A
static_assert(std::is_same_v<decltype(false ? memPtr : nullptr), int*A::*>);
// a.*memPtr теперь просто указатель на int и nullptr также становится указателем на int
static_assert(std::is_same_v<decltype(false ? a.*memPtr : nullptr), int*>);
  1. Такой условный оператор часто использовался в C++11 constexpr программировании до C++14.
  2. Доступ к членам , является ли функция преобразования удаленной (начиная с C++11) и является ли операнд битовым полем - игнорируются.

Тип результата условного оператора также доступен как бинарная типовая характеристика std::common_type .

(since C++11)

Перегрузки

Для каждой пары продвинутых арифметических типов L и R и для каждого типа P , где P является типом указателя, указателя на член или перечислением с областью видимости, следующие сигнатуры функций участвуют в разрешении перегрузки:

LR оператор ?: ( bool , L, R ) ;
P оператор ?: ( bool , P, P ) ;

где LR является результатом usual arithmetic conversions , применённых к 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> )
лексикографически сравнивает две строки, используя фасет сравнения данной локали
(публичная функция-член 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-to-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-to-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-to-rvalue применялись безусловно
в случае rvalue результата для условного оператора
применяется только в некоторых случаях
  1. Например, функции могут вызываться в инициализаторе переменной пространства имен, в этом контексте нет "вызывающей функции".

Смотрите также

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

Общие операторы
assignment increment
decrement
arithmetic logical comparison member
access
other

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
Специальные операторы

static_cast преобразует один тип в другой связанный тип
dynamic_cast преобразует в пределах иерархий наследования
const_cast добавляет или удаляет cv -квалификаторы
reinterpret_cast преобразует тип в несвязанный тип
C-style приведение преобразует один тип в другой с помощью комбинации static_cast , const_cast и reinterpret_cast
new создает объекты с динамической продолжительностью хранения
delete уничтожает объекты, ранее созданные выражением new, и освобождает полученную область памяти
sizeof запрашивает размер типа
sizeof... запрашивает размер пакета (since C++11)
typeid запрашивает информацию о типе
noexcept проверяет, может ли выражение генерировать исключение (since C++11)
alignof запрашивает требования выравнивания типа (since C++11)

Документация по C для Других операторов