成員訪問運算子
訪問其運算元的成員。
運算子名稱 | 語法 | 可過載 | 原型示例(對於 class T) | |
---|---|---|---|---|
類定義內部 | 類定義外部 | |||
下標 | a[b] | 是 | R& T::operator[](S b); | 不適用 |
a[...] (C++23 起) | R& T::operator[](...); | |||
解引用 | *a | 是 | R& T::operator*(); | R& operator*(T a); |
取址 | &a | 是 | R* T::operator&(); | R* operator&(T a); |
物件成員 | a.b | 否 | 不適用 | 不適用 |
指標成員 | a->b | 是 | R* T::operator->(); | 不適用 |
物件成員指標 | a.*b | 否 | 不適用 | 不適用 |
指標成員指標 | a->*b | 是 | R& T::operator->*(S b); | R& operator->*(T a, S b); |
|
目錄 |
[編輯] 解釋
內建“下標”運算子提供對由指標或陣列運算元指向的物件進行訪問。
內建“解引用”運算子提供對由指標運算元指向的物件或函式進行訪問。
內建“取址”運算子建立指向物件或函式運算元的指標。
“物件成員”和“物件成員指標”運算子提供對物件運算元的資料成員或成員函式的訪問。
內建“指標成員”和“指標成員指標”運算子提供對由指標運算元指向的類的資料成員或成員函式的訪問。
[編輯] 內建下標運算子
下標運算子表示式具有以下形式
expr1 [ expr2 ] |
(1) | ||||||||
expr1 [{ expr , ... }] |
(2) | (C++11 起) | |||||||
expr1 [ expr2 , expr , ... ] |
(3) | (C++23 起) | |||||||
T
。expr2 不能是沒有括號的逗號表示式。(C++23 起)內建下標表達式 E1[E2] 與表示式 *(E1 + E2) 完全相同,除了它的值類別(見下文)和求值順序(C++17 起):指標運算元(可能是陣列到指標轉換的結果,並且必須指向某個陣列的一個元素或越過其末尾一個位置)根據指標算術規則調整以指向同一陣列的另一個元素,然後被解引用。
當應用於陣列時,下標表達式是左值如果陣列是左值,並且是亡值如果不是(C++11 起)。
當應用於指標時,下標表達式始終是左值。
型別 T
不允許是不完整型別,即使 T
的大小或內部結構從未被使用,如 &x[0] 中所示。
使用沒有括號的逗號表示式作為下標運算子的第二個(右側)引數已棄用。 例如,a[b, c] 已棄用,而 a[(b, c)] 未棄用。 |
(C++20 起) (直至 C++23) |
沒有括號的逗號表示式不能作為下標運算子的第二個(右側)引數。例如,a[b, c] 要麼是格式錯誤的,要麼等價於 a.operator[](b, c)。 需要括號才能使用逗號表示式作為下標,例如 a[(b, c)]。 |
(C++23 起) |
在針對使用者定義運算子的過載決議中,對於每個物件型別 T
(可能帶 cv-限定符),以下函式簽名參與過載決議:
T& operator[](T*, std::ptrdiff_t); |
||
T& operator[](std::ptrdiff_t, T*); |
||
#include <iostream> #include <map> #include <string> int main() { int a[4] = {1, 2, 3, 4}; int* p = &a[2]; std::cout << p[1] << p[-1] << 1[p] << (-1)[p] << '\n'; std::map<std::pair<int, int>, std::string> m; m[{1, 2}] = "abc"; // uses the [{...}] version }
輸出
4242
[編輯] 內建解引用運算子
解引用運算子表示式具有以下形式
* expr |
|||||||||
內建解引用運算子的運算元必須是指向物件或指向函式的指標,結果是引用 expr 所指向的物件或函式的左值。如果 expr 未實際指向物件或函式,則行為未定義(除了 typeid
指定的情況)。
指向(可能帶 cv 限定符的)void 的指標不能被解引用。指向其他不完整型別的指標可以被解引用,但結果左值只能在允許不完整型別左值的上下文中使用,例如在初始化引用時。
在針對使用者定義運算子的過載決議中,對於每個是物件型別(可能帶 cv 限定符)或函式型別(不帶 const 或 ref 限定符)的型別 T
,以下函式簽名參與過載決議:
T& operator*(T*); |
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // lvalue can be bound to a reference int m = *pn; // indirection + lvalue-to-rvalue conversion int (*fp)() = &f; int (&fr)() = *fp; // function lvalue can be bound to a reference [](...){}(r, m, fr); // removes possible "unused variable" warnings }
[編輯] 內建取址運算子
取址運算子表示式具有以下形式
& expr |
(1) | ||||||||
& class :: member |
(2) | ||||||||
T
的左值表示式,operator&
建立並返回型別為 T*
的純右值,具有相同的 cv 限定符,指向運算元所指定的物件或函式。如果運算元具有不完整型別,可以形成指標,但如果該不完整型別碰巧是定義了自己的 operator& 的類,則內建運算子或過載運算子的使用是未指定的。對於帶有使用者定義 operator& 的型別的運算元,可以使用 std::addressof 獲取真實指標。請注意,與 C99 及更高版本的 C 不同,對一元 operator* 的結果應用一元 operator& 沒有特殊情況。
如果 expr 命名了一個顯式物件成員函式,則 expr 必須是一個限定識別符號。對命名顯式物件成員函式的非限定識別符號應用 |
(C++23 起) |
T
的類 C
的成員函式的純右值成員函式指標或資料成員指標。請注意,&member、C::member 甚至 &(C::member) 都不能用於初始化成員指標。在針對使用者定義運算子的過載決議中,此運算子不引入任何額外的函式簽名:如果存在一個可行的過載 operator&,則內建取址運算子不適用。
void f(int) {} void f(double) {} struct A { int i; }; struct B { void f(); }; int main() { int n = 1; int* pn = &n; // pointer int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // pointer to data member void (B::*mpf)() = &B::f; // pointer to member function void (*pf)(int) = &f; // overload resolution due to initialization context // auto pf2 = &f; // error: ambiguous overloaded function type auto pf2 = static_cast<void (*)(int)>(&f); // overload resolution due to cast }
[編輯] 內建成員訪問運算子
成員訪問運算子表示式具有以下形式
expr .template (可選) id-expr |
(1) | ||||||||
expr ->template (可選) id-expr |
(2) | ||||||||
expr . 偽解構函式 |
(3) | ||||||||
expr -> 偽解構函式 |
(4) | ||||||||
T*
的指標表示式。id-expr 是 T
的資料成員或成員函式,或 T
的無歧義且可訪問的基類 B
的資料成員或成員函式的名稱(形式上是一個識別符號表示式,它命名),可選地限定(例如 E1.B::E2 或 E1->B::E2),可選地使用template 消歧符(例如 E1.template E2 或 E1->template E2)。
如果呼叫了使用者定義的 operator->,則會遞迴地對結果值再次呼叫 operator->,直到達到返回純指標的 operator->。此後,內建語義應用於該指標。
表示式 E1->E2 對於內建型別與 (*E1).E2 完全等價;這就是為什麼以下規則只處理 E1.E2。
在表示式 E1.E2 中
- 如果 E2 是引用型別
T&
或T&&
(C++11 起),則結果是型別為T
的左值,指定引用所繫結的物件或函式。 - 否則,給定 E2 的型別為
T
,則結果是型別為T
的左值,指定該靜態資料成員。
- 如果 E2 是引用型別
T&
或T&&
(C++11 起),則結果是型別為T
的左值,指定 E1 的相應引用成員所繫結的物件或函式。 - 否則,如果 E1 是左值,則結果是指定 E1 的該非靜態資料成員的左值。
- 否則(如果 E1 是右值(C++17 前)亡值(可能是從純右值實體化而來的)(C++17 起)),則結果是指定 E1 的該非靜態資料成員的右值(C++11 前)亡值(C++11 起)。
T
,則結果是型別為 T
的右值(C++11 前)純右值(C++11 起),其值是列舉器的值。~
後跟型別名或指定相同型別(減去 cv-限定符)的decltype 說明符,可選地限定,則結果是一種特殊的純右值,只能用作函式呼叫運算子的左運算元,不能用於其他目的。operator. 不能過載,對於 operator->,在針對使用者定義運算子的過載決議中,內建運算子不引入任何額外的函式簽名:如果存在一個可行的過載 operator->,則內建 operator-> 不適用。
#include <cassert> #include <iostream> #include <memory> struct P { template<typename T> static T* ptr() { return new T; } }; template<typename T> struct A { A(int n): n(n) {} int n; static int sn; int f() { return 10 + n; } static int sf() { return 4; } class B {}; enum E {RED = 1, BLUE = 2}; void g() { typedef int U; // keyword template needed for a dependent template member int* p = T().template ptr<U>(); p->~U(); // U is int, calls int's pseudo destructor delete p; } }; template<> int A<P>::sn = 2; struct UPtrWrapper { std::unique_ptr<std::string> uPtr; std::unique_ptr<std::string>& operator->() { return uPtr; } }; int main() { A<P> a(1); std::cout << a.n << ' ' << a.sn << ' ' // A::sn also works << a.f() << ' ' << a.sf() << ' ' // A::sf() also works // << &a.f << ' ' // error: ill-formed if a.f is not the // left-hand operand of operator() // << a.B << ' ' // error: nested type not allowed << a.RED << ' '; // enumerator UPtrWrapper uPtrWrap{std::make_unique<std::string>("wrapped")}; assert(uPtrWrap->data() == uPtrWrap.operator->().operator->()->data()); }
輸出
1 2 11 4 1
如果 E2 是非靜態成員,並且 E1 的結果是一個物件,其型別與 E1 的型別不相似,則行為未定義。
struct A { int i; }; struct B { int j; }; struct D : A, B {}; void f() { D d; static_cast<B&>(d).j; // OK, object expression designates the B subobject of d reinterpret_cast<B&>(d).j; // undefined behavior }
[編輯] 內建成員指標訪問運算子
透過成員指標的成員訪問運算子表示式具有以下形式
lhs .* rhs |
(1) | ||||||||
lhs ->* rhs |
(2) | ||||||||
T
的表示式。T*
的指標表示式。rhs 必須是型別為指向 T
的成員(資料或函式)的指標或指向 T
的無歧義且可訪問的基類 B
的成員的指標的右值。
表示式 E1->*E2 對於內建型別與 (*E1).*E2 完全等價;這就是為什麼以下規則只處理 E1.*E2。
在表示式 E1.*E2 中
- 如果 E1 是左值,則結果是指定該資料成員的左值,
- 否則(如果 E1 是右值(C++17 前)亡值(可能是從純右值實體化而來的)(C++17 起)),則結果是指定該資料成員的右值(C++11 前)亡值(C++11 起);
&
的成員函式,則程式格式錯誤,除非該成員函式具有 cv-限定符 const 但沒有 volatile(C++20 起);
7) 如果 E1 是左值,且 E2 指向帶有 ref-限定符
&& 的成員函式,則程式格式錯誤。 |
(C++11 起) |
在針對使用者定義運算子的過載決議中,對於型別 D
、B
、R
的每種組合,其中類型別 B
要麼與 D
相同,要麼是 D
的無歧義且可訪問的基類,並且 R
要麼是物件型別,要麼是函式型別,以下函式簽名參與過載決議
R& operator->*(D*, R B::*); |
||
其中兩個運算元都可以是 cv-限定的,在這種情況下,返回型別的 cv-限定是運算元的 cv-限定的並集。
#include <iostream> struct S { S(int n) : mi(n) {} mutable int mi; int f(int n) { return mi + n; } }; struct D : public S { D(int n) : S(n) {} }; int main() { int S::* pmi = &S::mi; int (S::* pf)(int) = &S::f; const S s(7); // s.*pmi = 10; // error: cannot modify through mutable std::cout << s.*pmi << '\n'; D d(7); // base pointers work with derived object D* pd = &d; std::cout << (d.*pf)(7) << ' ' << (pd->*pf)(8) << '\n'; }
輸出
7 14 15
[編輯] 標準庫
下標運算子被許多標準容器類過載
訪問特定位 ( std::bitset<N> 的公共成員函式) | |
提供對託管陣列的索引訪問 ( std::unique_ptr<T,Deleter> 的公共成員函式) | |
訪問指定的字元 ( std::basic_string<CharT,Traits,Allocator> 的公共成員函式) | |
訪問指定的元素 ( std::array<T,N> 的公共成員函式) | |
訪問指定的元素 ( std::deque<T,Allocator> 的公共成員函式) | |
訪問指定的元素 ( std::vector<T,Allocator> 的公共成員函式) | |
訪問或插入指定元素 ( std::map<Key,T,Compare,Allocator> 的公共成員函式) | |
訪問或插入指定元素 ( std::unordered_map<Key,T,Hash,KeyEqual,Allocator> 的公共成員函式) | |
透過索引訪問元素 ( std::reverse_iterator<Iter> 的公共成員函式) | |
透過索引訪問元素 ( std::move_iterator<Iter> 的公共成員函式) | |
獲取/設定 valarray 元素、切片或掩碼 ( std::valarray<T> 的公共成員函式) | |
返回指定的子匹配 ( std::match_results<BidirIt,Alloc> 的公共成員函式) |
解引用和成員運算子被許多迭代器和智慧指標類過載
解引用指向託管物件的指標 ( std::unique_ptr<T,Deleter> 的公共成員函式) | |
解引用儲存的指標 ( std::shared_ptr<T> 的公共成員函式) | |
訪問託管物件 ( std::auto_ptr<T> 的公共成員函式) | |
解引用迭代器 ( std::raw_storage_iterator<OutputIt,T> 的公共成員函式) | |
解引用遞減後的底層迭代器 ( std::reverse_iterator<Iter> 的公共成員函式) | |
無操作 ( std::back_insert_iterator<Container> 的公共成員函式) | |
無操作 ( std::front_insert_iterator<Container> 的公共成員函式) | |
無操作 ( std::insert_iterator<Container> 的公共成員函式) | |
訪問指向的元素 ( std::move_iterator<Iter> 的公共成員函式) | |
返回當前元素 ( std::istream_iterator<T,CharT,Traits,Distance> 的公共成員函式) | |
無操作 ( std::ostream_iterator<T,CharT,Traits> 的公共成員函式) | |
獲取當前字元的副本 ( std::istreambuf_iterator<CharT,Traits> 的公共成員函式) | |
無操作 ( std::ostreambuf_iterator<CharT,Traits> 的公共成員函式) | |
訪問當前匹配 ( std::regex_iterator<BidirIt,CharT,Traits> 的公共成員函式) | |
訪問當前子匹配 ( std::regex_token_iterator<BidirIt,CharT,Traits> 的公共成員函式) |
沒有標準庫類過載 operator&。過載 operator& 最著名的例子是 Microsoft COM 類 CComPtr
,儘管它也可以出現在 EDSLs 中,例如 boost.spirit。
沒有標準庫類過載 operator->*。曾有人建議它可以作為智慧指標介面的一部分,實際上在 boost.phoenix 中被 actors 以此身份使用,但在 EDSLs 中更常見,例如 cpp.react。
[編輯] 註解
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_multidimensional_subscript |
202110L |
(C++23) | 多維下標運算子 |
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 1213 | C++11 | 對陣列右值進行下標操作會產生左值 | 重新分類為亡值 |
CWG 1458 | C++98 | 對聲明瞭 operator& 的不完整類型別的左值應用 & 會導致未定義行為未定義行為 |
未指定 使用哪個 & |
CWG 1642 | C++98 | 內建成員指標訪問運算子中的 rhs 可以是左值 | 只能是右值 |
CWG 1800 | C++98 | 對成員匿名聯合的非靜態資料成員應用 & 時,不清楚匿名聯合是否參與結果型別不清楚 匿名聯合 |
匿名聯合 不包含在 結果型別中 |
CWG 2614 | C++98 | 如果 E2 是引用成員或列舉器,則 E1.E2 的結果不明確 | 已明確 |
CWG 2725 | C++98 | 如果 E2 是靜態成員函式,則 E1.E2 格式正確 即使它不是 operator() 的左運算元 |
E1.E2 格式錯誤 在這種情況下 |
CWG 2748 | C++98 | 如果 E1 是空指標且 E2 引用靜態成員,則 E1->E2 的行為不明確 空指標,行為不明確 |
在這種情況下,行為是 未定義的 |
CWG 2813 | C++98 | 如果 E1.E2 命名靜態成員或列舉,則 E1 不是丟棄值表示式 命名靜態成員或列舉 |
它是 |
CWG 2823 | C++98 | 如果 expr 未指向物件或函式,則 *expr 的行為不明確 未指向物件或函式 |
已明確 |
[編輯] 另請參閱
常見運算子 | ||||||
---|---|---|---|---|---|---|
賦值 | 遞增 遞減 |
算術 | 邏輯 | 比較 | 成員 訪問 |
其他 |
a = b |
++a |
+a |
!a |
a == b |
a[...] |
函式呼叫 a(...) |
逗號 a, b | ||||||
條件 a ? b : c | ||||||
特殊運算子 | ||||||
static_cast 將一種型別轉換為另一種相關型別 |
C 文件 關於 成員訪問運算子
|