其他運算子
運算子 名稱 |
語法 | 可過載 | 原型示例(對於 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
|
否 | 不適用 | 不適用 |
“函式呼叫”運算子為任何物件提供函式語義。
“條件運算子”(俗稱“三元條件運算子”)檢查第一個表示式的布林值,並根據結果值,評估並返回第二個或第三個表示式。
目錄 |
[編輯] 內建函式呼叫運算子
函式呼叫表示式具有以下形式:
function ( arg1, arg2, arg3, ...) |
|||||||||
function | - | 函式型別或函式指標型別的表示式 |
arg1, arg2, arg3, ... |
- | 可能為空的任意表達式列表或花括號括起來的初始化器列表(C++11 起),但在頂層不允許使用逗號運算子以避免歧義 |
對於非成員函式或靜態成員函式的呼叫,function可以是引用函式的左值(在這種情況下會抑制函式到指標的轉換),也可以是函式指標型別的純右值。
function指定的函式(或成員)名稱可以過載,過載決議規則用於決定呼叫哪個過載。
如果function指定的是成員函式,它可以是虛擬函式,在這種情況下,將使用執行時動態派發呼叫該函式的最終覆蓋器。
每個函式引數在必要時經過隱式轉換後,用其對應的實參進行初始化。
- 如果沒有對應的實參,則使用對應的預設實參;如果也沒有,則程式格式錯誤。
- 如果呼叫的是成員函式,則當前物件的this指標將被轉換,如同透過顯式轉換,以符合函式所期望的this指標型別。
- 每個引數的初始化和銷燬發生在函式調用出現的完整表示式的上下文中,這意味著,例如,如果引數的建構函式或解構函式丟擲異常,則不會考慮被呼叫函式的函式try塊。
如果函式是變長引數函式,則對由省略號引數匹配的所有實參應用預設實參提升。
引數是在定義它的函式退出時銷燬,還是在包含它的完整表示式結束時銷燬,這是實現定義的。引數總是以其構造的逆序銷燬。
函式呼叫表示式的返回型別是所選函式的返回型別,使用靜態繫結決定(忽略virtual關鍵字),即使實際呼叫的覆蓋函式返回不同的型別。這允許覆蓋函式返回指向派生自基函式返回型別的類的指標或引用,即 C++ 支援協變返回型別。如果function指定的是解構函式,則返回型別為void。
當類型別`X`的物件作為函式引數傳遞或從函式返回時,如果`X`的每個複製建構函式、移動建構函式和解構函式都是平凡的或已刪除的,並且`X`至少有一個未刪除的複製或移動建構函式,則允許實現建立一個臨時物件來儲存函式引數或結果物件。 臨時物件分別從函式實參或返回值構造,並且函式的引數或返回物件被初始化,如同使用未刪除的平凡建構函式複製臨時物件一樣(即使該建構函式不可訪問或不會被過載決議選擇來執行物件的複製或移動)。 這允許將小型類型別的物件(例如std::complex或std::span)透過暫存器傳遞給函式或從函式返回。 |
(C++17 起) |
如果函式返回左值引用或函式右值引用,則函式呼叫表示式的值類別為左值;如果函式返回物件右值引用,則為亡值;否則為純右值。如果函式呼叫表示式是物件型別的純右值,則它必須具有完整型別,除非用作decltype
的運算元(或作為decltype運算元的內建逗號運算子的右運算元)(C++11 起)。
當被呼叫函式正常退出時,函式的所有後置條件斷言將按順序評估。如果實現引入任何臨時物件來儲存結果值,對於每個後置條件斷言的評估 |
(C++26 起) |
函式呼叫表示式在語法上與值初始化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)。
不帶括號的逗號表示式作為下標運算子的第二個(右側)實參已棄用。 例如,a[b, c]已棄用,而a[(b, c)]則沒有。 |
(C++20 起) (直至 C++23) |
不帶括號的逗號表示式不能作為下標運算子的第二個(右側)引數。例如,a[b, c]要麼格式錯誤,要麼等同於a.operator[](b, c)。 當使用逗號表示式作為下標時,需要括號,例如a[(b, c)]。 |
(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型別,則結果是一個右值(C++11 之前)一個純右值(C++11 起),型別為void。
如果E2和E3中只有一個是void型別
如果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是左值位域(C++11 之前)相同值類別的泛左值位域(C++11 起),且分別具有型別 cv1 T
和 cv2 T
,則運算元在剩餘過程中被視為型別為 cv T
,其中 cv 是 cv1 和 cv2 的並集。
如果E2和E3具有不同的型別,並且滿足以下任何條件,則進入階段 3
- E2和E3中至少有一個是(可能帶cv限定符的)類型別。
- E2和E3都是相同型別的左值(C++11 之前)相同值類別和相同型別的泛左值(C++11 起),除了cv限定符。
否則,繼續階段 4。
[編輯] 階段 3
嘗試從型別為TX
的運算元表示式X到與運算元表示式Y的型別TY
相關的目標型別形成隱式轉換序列[2],如下所示:
- 如果Y是左值,則目標型別是
TY&
,但只有當引用能夠直接繫結到左值(C++11 之前)泛左值(C++11 起)時,才能形成隱式轉換序列。
|
(C++11 起) |
- 如果Y是右值(C++11 之前)純右值(C++11 起),或者如果上述轉換序列都不能形成,並且
TX
和TY
中至少有一個是(可能帶有cv限定符的)類型別- 如果
TX
和TY
是相同的類型別(忽略cv限定符)- 如果
TY
的cv限定符至少與TX
的相同,則目標型別是TY
。 - 否則,不形成轉換序列。
- 如果
- 否則,如果
TY
是TX
的基類,則目標型別是具有TX
的cv限定符的TY
。 - 否則,目標型別是Z的型別,其中Z是應用左值到右值、陣列到指標和函式到指標標準轉換後Y的值。
- 如果
- 否則,不形成轉換序列。
使用此過程,確定是否可以從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
如果E2和E3是相同型別的左值,則結果是該型別的左值,並且如果E2和E3中至少有一個是位域,則結果也是位域。 |
(C++11 前) |
如果E2和E3是相同型別和相同值類別的泛左值,則結果具有相同的型別和值類別,並且如果E2和E3中至少有一個是位域,則結果也是位域。 |
(C++11 起) |
否則,結果是一個右值(C++11 之前)一個純右值(C++11 起)。
- 如果E2和E3的型別不同,並且其中任何一個具有(可能帶有cv限定符的)類型別,則進入階段 5。
- 否則,進入階段 6。
[編輯] 階段 5
- 如果過載決議失敗,則程式格式錯誤。
- 否則,應用所選轉換,並將轉換後的運算元用於剩餘過程。進入下一階段。
[編輯] 階段 6
將陣列到指標和函式到指標的轉換應用於(可能已轉換的)E2和E3。完成這些轉換後,必須滿足以下至少一個條件,否則程式格式錯誤:
- E2和E3具有相同的型別。在這種情況下,結果是該型別,並且使用所選運算元進行複製初始化。
- E2和E3都具有算術或列舉型別。在這種情況下,應用常用算術轉換將它們轉換為其公共型別,結果是該型別。
- E2和E3中至少有一個是指標。在這種情況下,應用左值到右值、指標、函式指標(C++17 起)和限定符轉換,將它們轉換為其複合指標型別,結果是該型別。
- E2和E3中至少有一個是成員指標。在這種情況下,應用左值到右值、成員指標、函式指標(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++14 之前,這種條件運算子通常用於 C++11 constexpr 程式設計中。
- ↑ 忽略成員訪問、轉換函式是否已刪除(C++11 起)以及運算元是否是位域。
條件運算子的結果型別也可以透過二元型別特性std::common_type訪問。 |
(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> 的公共成員函式) | |
返回兩個實參的邏輯 AND ( std::logical_and<T> 的公共成員函式) | |
返回兩個實參的邏輯 OR ( 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 facet 對兩個字串進行字典比較 ( 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 庫在 boost.assign、boost.spirit 和其他庫中使用 operator,。資料庫訪問庫 SOCI 也過載 operator,。
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 446 | C++98 | 對於條件運算子上的左值到右值轉換,是否建立臨時物件未指定 對於條件運算子上的左值到右值轉換,是否建立臨時物件未指定 |
如果運算子返回類右值 則總是建立臨時物件 |
CWG 462 | C++98 | 如果逗號運算子的第二個運算元是臨時物件, 當逗號表示式的結果繫結到引用時,其生命週期是否會延長未指定 當逗號表示式的結果繫結到引用時,其生命週期是否會延長未指定 |
在這種情況下,逗號表示式的結果 是臨時物件 (因此其生命週期得以延長) |
CWG 587 | C++98 | 當條件運算子的第二和第三個運算元是具有相同型別(除了 cv 限定符)的左值時,如果這些運算元 具有類型別,則結果為左值,否則為右值 具有類型別,則結果為左值,否則為右值 |
在這種情況下,結果總是 左值 |
CWG 1029 | C++98 | 解構函式呼叫的型別未指定 | 指定為 void |
CWG 1550 | C++98 | 如果其他運算元是非 void 型別,則條件表示式中不允許帶括號的 throw 表示式 如果其他運算元是非 void 型別,則條件表示式中不允許帶括號的 throw 表示式 |
允許 |
CWG 1560 | C++98 | 條件運算子的 void 運算元導致 對其他運算元進行無用的左值到右值轉換,總是導致右值 對其他運算元進行無用的左值到右值轉換,總是導致右值 |
帶有 void 的條件表示式 可以是左值 |
CWG 1642 | C++98 | 函式呼叫表示式中的 function 表示式 可以是函式指標左值 |
不允許 |
CWG 1805 | C++98 | 在確定隱式轉換序列的目標型別時, 將 Y 轉換為 Z 的方式不明確 |
已明確 |
CWG 1895 | C++98 C++11 |
不清楚已刪除 (C++11) 或不可訪問 (C++98) 轉換函式是否阻止條件表示式中的轉換, 以及是否未考慮從基類到派生類純右值的轉換 以及是否未考慮從基類到派生類純右值的轉換 |
像過載決議一樣處理 過載決議 |
CWG 1932 | C++98 | 條件表示式中缺少同類型位域 | 由底層型別處理 |
CWG 2226 | C++11 | 在確定條件運算子的其他運算元 的目標型別時,如果該運算元是左值,則引用 無法繫結到 xvalue |
允許 |
CWG 2283 | C++17 | 函式呼叫運算子的型別完整性要求被 P0135R1 意外移除 函式呼叫運算子的型別完整性要求被 P0135R1 意外移除 |
恢復了要求 |
CWG 2321 | C++98 | 在確定條件運算子的其他運算元 的目標型別時,派生類型別無法 轉換為 cv 限定符較少的基類型別 |
允許轉換為基類型別, 使用來自派生類運算元的 cv 限定符 使用來自派生類運算元的 cv 限定符 |
CWG 2715 | C++98 | 每個引數的初始化和析構將發生 在呼叫函式的上下文中,這可能不存在[1] 在呼叫函式的上下文中,這可能不存在[1] |
發生在 封閉的完整表示式的上下文中 |
CWG 2850 | C++98 | 引數的析構順序不明確 | 已明確 |
CWG 2865 | C++98 | 如果 TX 和 TY 是相同的類型別,並且 TX 的 cv 限定符比 TY 多,則仍然可以從純右值 Y 形成隱式轉換序列比 TY 多,則仍然可以從純右值 Y 形成隱式轉換序列 |
在這種情況下不會形成 轉換序列 |
CWG 2906 | C++98 | 在條件運算子的右值結果情況下,無條件地應用左值到右值轉換 在條件運算子的右值結果情況下,無條件地應用左值到右值轉換 |
僅在某些情況下應用 |
- ↑ 例如,函式可以在名稱空間範圍變數的初始化器中呼叫,在這種情況下沒有“呼叫函式”。
[編輯] 另請參閱
常見運算子 | ||||||
---|---|---|---|---|---|---|
賦值 | 遞增 遞減 |
算術 | 邏輯 | 比較 | 成員 訪問 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
函式呼叫 a(...) |
逗號 a, b | ||||||
條件運算子 a ? b : c | ||||||
特殊運算子 | ||||||
static_cast 將一種型別轉換為另一種相關型別 |
C 文件 關於 其他運算子
|