名稱空間
變體
操作

成員訪問運算子

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
表示式
替代表示
字面量
布林字面量 - 整數字面量 - 浮點字面量
字元字面量 - 字串字面量 - nullptr (C++11)
使用者定義 (C++11)
工具
屬性 (C++11)
型別
typedef 宣告
類型別名宣告 (C++11)
型別轉換
記憶體分配
類特有的函式屬性
explicit (C++11)
static

特殊成員函式
模板
雜項
 
 

訪問其運算元的成員。

  運算子名稱             語法           可過載 原型示例(對於 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);
注意
  • 與大多數使用者定義過載一樣,返回型別應與內建運算子提供的返回型別匹配,以便可以與內建運算子以相同方式使用使用者定義的運算子。但是,在使用者定義的運算子過載中,任何型別都可以用作返回型別(包括 void)。一個例外是 operator->,它必須返回一個指標或另一個過載了 operator-> 的類才能真正可用。

目錄

[編輯] 解釋

內建“下標”運算子提供對由指標陣列運算元指向的物件進行訪問。

內建“解引用”運算子提供對由指標運算元指向的物件或函式進行訪問。

內建“取址”運算子建立指向物件或函式運算元的指標。

“物件成員”和“物件成員指標”運算子提供對物件運算元的資料成員或成員函式的訪問。

內建“指標成員”和“指標成員指標”運算子提供對由指標運算元指向的類的資料成員或成員函式的訪問。

[編輯] 內建下標運算子

下標運算子表示式具有以下形式

expr1 [expr2 ] (1)
expr1 [{expr , ...}] (2) (C++11 起)
expr1 [expr2 , expr , ...] (3) (C++23 起)
1) 對於內建運算子,其中一個表示式(expr1expr2)必須是“T 陣列”型別的左值或“指向 T 的指標”型別的純右值,而另一個表示式(分別為 expr2expr1)必須是無作用域列舉型別或整型的純右值。此表示式的結果型別為 Texpr2 不能是沒有括號的逗號表示式(C++23 起)
2) 方括號內帶有花括號括起來的列表的形式僅用於呼叫過載的 operator[]
3) 方括號內帶有逗號分隔的表示式列表的形式僅用於呼叫過載的 operator[]

內建下標表達式 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)
1) 如果運算元是某種物件或函式型別 T 的左值表示式,operator& 建立並返回型別為 T* 的純右值,具有相同的 cv 限定符,指向運算元所指定的物件或函式。如果運算元具有不完整型別,可以形成指標,但如果該不完整型別碰巧是定義了自己的 operator& 的類,則內建運算子或過載運算子的使用是未指定的。對於帶有使用者定義 operator& 的型別的運算元,可以使用 std::addressof 獲取真實指標。請注意,與 C99 及更高版本的 C 不同,對一元 operator* 的結果應用一元 operator& 沒有特殊情況。
如果運算元是過載函式的名稱,則只有在透過上下文可以解決過載時才能取址。詳見過載函式的地址

如果 expr 命名了一個顯式物件成員函式,則 expr 必須是一個限定識別符號。對命名顯式物件成員函式的非限定識別符號應用 & 是格式錯誤的。

(C++23 起)
2) 如果運算元是非靜態或變體成員的限定名,而不是顯式物件成員函式(C++23 起),例如 &C::member,結果是型別為 T 的類 C 的成員函式的純右值成員函式指標資料成員指標。請注意,&memberC::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)
1) expr 必須是完整類型別 T 的表示式。
如果 id-expr 命名一個靜態成員列舉器,則 expr 是一個丟棄值表示式
2) expr 必須是指向完整類型別 T* 的指標表示式。
3,4) expr 必須是標量型別表示式(見下文)。

id-exprT 的資料成員或成員函式,或 T 的無歧義且可訪問的基類 B 的資料成員或成員函式的名稱(形式上是一個識別符號表示式,它命名),可選地限定(例如 E1.B::E2E1->B::E2),可選地使用template 消歧符(例如 E1.template E2E1->template E2)。

如果呼叫了使用者定義的 operator->,則會遞迴地對結果值再次呼叫 operator->,直到達到返回純指標的 operator->。此後,內建語義應用於該指標。

表示式 E1->E2 對於內建型別與 (*E1).E2 完全等價;這就是為什麼以下規則只處理 E1.E2

在表示式 E1.E2

1) 如果 E2靜態資料成員
  • 如果 E2 是引用型別 T& T&&(C++11 起),則結果是型別為 T 的左值,指定引用所繫結的物件或函式。
  • 否則,給定 E2 的型別為 T,則結果是型別為 T 的左值,指定該靜態資料成員。
本質上,E1 在兩種情況下都會被求值並丟棄。
2) 如果 E2非靜態資料成員
  • 如果 E2 是引用型別 T& T&&(C++11 起),則結果是型別為 T 的左值,指定 E1 的相應引用成員所繫結的物件或函式。
  • 否則,如果 E1 是左值,則結果是指定 E1 的該非靜態資料成員的左值。
  • 否則(如果 E1右值(C++17 前)亡值(可能是從純右值實體化而來的)(C++17 起)),則結果是指定 E1 的該非靜態資料成員的右值(C++11 前)亡值(C++11 起)
如果 E2 不是可變成員,則結果的 cv-限定符E1E2 的 cv-限定符的並集;否則(如果 E2 是可變成員),它是 E1E2 的 volatile-限定符的並集。
3) 如果 E2 是過載集(一個或多個靜態成員函式非靜態成員函式),則 E1.E2 必須是成員函式呼叫運算子的(可能帶括號的)左運算元,並且使用函式過載決議來選擇 E2 引用的函式,之後
  • 如果 E2靜態成員函式,則結果是指定該靜態成員函式的左值。本質上,E1 在這種情況下被求值並丟棄。
  • 否則(E2非靜態成員函式),結果是指定 E1 的該非靜態成員函式的純右值。
4) 如果 E2 是成員列舉器,給定 E2 的型別為 T,則結果是型別為 T右值(C++11 前)純右值(C++11 起),其值是列舉器的值。
5) 如果 E2巢狀型別,則程式格式錯誤。
6) 如果 E1 具有ScalarType,且 E2~ 後跟型別名或指定相同型別(減去 cv-限定符)的decltype 說明符,可選地限定,則結果是一種特殊的純右值,只能用作函式呼叫運算子的左運算元,不能用於其他目的。
結果函式呼叫表示式稱為“偽解構函式呼叫”。它不帶任何引數,返回 void,計算 E1,並終止其結果物件的生命週期。這是 operator. 的左運算元具有非類型別的唯一情況。允許偽解構函式呼叫使得無需知道給定型別是否存在解構函式就可以編寫程式碼。

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)
1) lhs 必須是類型別 T 的表示式。
2) lhs 必須是指向類型別 T* 的指標表示式。

rhs 必須是型別為指向 T 的成員(資料函式)的指標或指向 T 的無歧義且可訪問的基類 B 的成員的指標的右值。

表示式 E1->*E2 對於內建型別與 (*E1).*E2 完全等價;這就是為什麼以下規則只處理 E1.*E2

在表示式 E1.*E2

1) 如果 E2 是資料成員指標,
  • 如果 E1 是左值,則結果是指定該資料成員的左值,
  • 否則(如果 E1右值(C++17 前)亡值(可能是從純右值實體化而來的)(C++17 起)),則結果是指定該資料成員的右值(C++11 前)亡值(C++11 起)
2) 如果 E2 是成員函式指標,則結果是一種特殊的純右值,指定該成員函式,只能用作成員函式呼叫運算子的左運算元,不能用於其他目的;
3) cv-限定規則與物件成員運算子相同,但有一條附加規則:指向可變成員的成員指標不能用於修改 const 物件中的該成員;
4) 如果 E2 是空成員指標值,則行為未定義;
5) 如果 E1 的結果是一個物件,其型別與 E1 的型別不相似,或者其最派生物件不包含 E2 引用的成員,則行為未定義;
6) 如果 E1 是右值,且 E2 指向帶有 ref-限定符 & 的成員函式,則程式格式錯誤,除非該成員函式具有 cv-限定符 const 但沒有 volatile(C++20 起)
7) 如果 E1 是左值,且 E2 指向帶有 ref-限定符 && 的成員函式,則程式格式錯誤。
(C++11 起)

針對使用者定義運算子的過載決議中,對於型別 DBR 的每種組合,其中類型別 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 += 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 風格轉換 透過 static_castconst_castreinterpret_cast 的組合將一種型別轉換為另一種型別
new 建立具有動態儲存期的物件
delete 銷燬先前由 new 表示式建立的物件並釋放獲得的記憶體區域
sizeof 查詢型別的大小
sizeof... 查詢 的大小 (C++11 起)
typeid 查詢型別的型別資訊
noexcept 檢查表示式是否可以丟擲異常 (C++11 起)
alignof 查詢型別的對齊要求 (C++11 起)

C 文件 關於 成員訪問運算子