メンバアクセス演算子
被演算子のメンバにアクセスします。
演算子の名前 | 構文 | オーバーロード可能 | プロトタイプの例 (class T に対して) | |
---|---|---|---|---|
クラス定義の内側 | クラス定義の外側 | |||
添字 | a[b]
|
Yes | R& T::operator[](S b); | N/A |
間接参照 | *a
|
Yes | R& T::operator*(); | R& operator*(T a); |
アドレス取得 | &a
|
Yes | R* T::operator&(); | R* operator&(T a); |
オブジェクトのメンバ | a.b
|
No | N/A | N/A |
ポインタのメンバ | a->b
|
Yes | R* T::operator->(); | N/A |
オブジェクトのメンバへのポインタ | a.*b
|
No | N/A | N/A |
ポインタのメンバへのポインタ | a->*b
|
Yes | R& T::operator->*(S b); | R& operator->*(T a, S b); |
|
目次 |
[編集] 説明
組み込みの添字演算子はポインタまたは配列の被演算子が指す先のオブジェクトへのアクセスを提供します。
組み込みの間接参照演算子はポインタの被演算子が指す先のポインタまたは関数へのアクセスを提供します。
組み込みのアドレス取得演算子はオブジェクトまたは関数の被演算子を指すポインタを作成します。
オブジェクトのメンバおよびオブジェクトのメンバへのポインタ演算子はオブジェクトの被演算子のデータメンバまたはメンバ関数へのアクセスを提供します。
組み込みのポインタのメンバおよびポインタのメンバへのポインタ演算子はポインタの被演算子が指す先のクラスのデータメンバまたはメンバ関数へのアクセスを提供します。
[編集] 組み込みの添字演算子
添字演算子式は以下の形式を持ちます。
expr1 [ expr2 ]
|
(1) | ||||||||
expr1 [ { expr, ... } ]
|
(2) | (C++11) | |||||||
T
型です。operator[]
を呼ぶためにのみ使用できます。組み込みの添字式 E1[E2] は評価順序を除いて (C++17以上)式 *(E1 + E2) と正確に同一です。 つまり、そのポインタの被演算子 (配列からポインタへの変換の結果かもしれません) (何らかの配列の要素またはその最後の次を指していなければなりません) はポインタ算術のルールに従って同じ配列の別の要素を指すように調節され、そして逆参照されます。
配列に適用したとき、添字式は lvalue (その配列が lvalue の場合) または xvalue (そうでない場合) (C++11以上) です。
ポインタに適用したとき、添字式は常に lvalue です。
たとえ &x[0] のようにサイズや内部構造が使用されなくとも、型 T
は不完全型であることは許されていません。
ユーザ定義に演算子に対するオーバーロード解決において、すべてのオブジェクト型 (およびその cv 修飾された型) T
について、以下の関数シグネチャがオーバーロード解決に参加します。
T& operator[](T*, std::ptrdiff_t); |
||
T& operator[](std::ptrdiff_t, T*); |
||
#include <iostream> int main() { int a[4] = {1, 2, 3, 4}; int* p = &a[2]; std::cout << p[1] << p[-1] << 1[p] << (-1)[p] << '\n'; }
出力:
4242
[編集] 組み込みの間接参照演算子
間接参照演算子式は以下の形式を持ちます。
* expr
|
|||||||||
組み込みの間接参照演算子の被演算子はオブジェクトへのポインタまたは関数へのポインタでなければならず、結果は expr が指す先のポインタまたは関数を参照する lvalue です。
void (または cv 修飾された void) へのポインタは逆参照できません。 それ以外の不完全型へのポインタは逆参照できますが、結果の lvalue は不完全型の lvalue が許容される文脈 (参照を初期化するときなど) でのみ使用できます。
ユーザ定義の演算子に対するオーバーロード解決において、オブジェクト型 (またはその cv 修飾された型) または関数型 (const 修飾も ref 修飾もされていない) であるすべての型 T
について、以下の関数シグネチャがオーバーロード解決に参加します。
T& operator*(T*); |
||
#include <iostream> int f() { return 42; } int main() { int n = 1; int* pn = &n; int& r = *pn; // 左辺値は参照に束縛できます int m = *pn; // 間接参照+左辺値から右辺値への変換 int (*fp)() = &f; int (&fr)() = *fp; // 関数左辺値は参照に束縛できます }
[編集] 組み込みのアドレス取得演算子
アドレス取得演算子式は以下の形式を持ちます。
& expr
|
(1) | ||||||||
& class :: member
|
(2) | ||||||||
T
の lvalue 式である場合、 operator&
は、その被演算子が表すオブジェクトまたは関数を指す、同じ cv 修飾を持つ T*
型の prvalue を、作成して返します。 被演算子が不完全型の場合、ポインタは形成できますが、その不完全型がたまたま独自の operator&
を定義するクラスであった場合、動作は未定義です (C++14未満)組み込みとオーバーロードのどちらが使用されるかは未規定です (C++14以上)。 ユーザ定義の operator&
を持つ型の被演算子に対して真のポインタを取得するために std::addressof を使用することができます。
C99 以上の C と異なり、単項 operator*
の結果に単項 operator&
を適用した場合に対する特別扱いはありません。C
の型 T
のメンバ関数へのポインタまたはデータメンバへのポインタの prvalue です。 メンバへのポインタを初期化するためには、 &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; // ポインタ int* pn2 = &*pn; // pn2 == pn int A::* mp = &A::i; // データメンバへのポインタ void (B::*mpf)() = &B::f; // メンバ関数へのポインタ void (*pf)(int) = &f; // 初期化の文脈によるオーバーロード解決 // auto pf2 = &f; // エラー、オーバーロードされた関数の曖昧な型 auto pf2 = static_cast<void (*)(int)>(&f); // キャストによるオーバーロード解決 }
[編集] 組み込みのメンバアクセス演算子
メンバアクセス演算子式は以下の形式を持ちます。
expr . template (オプション) id-expr
|
(1) | ||||||||
expr -> template (オプション) id-expr
|
(2) | ||||||||
expr . pseudo-destructor
|
(3) | ||||||||
expr -> pseudo-destructor
|
(4) | ||||||||
T*
の式でなければなりません。どちらの演算子も1つめの被演算子はたとえ必要でなくとも評価されます (2つめの被演算子が静的メンバを表すときなど)。
どちらの演算子も2つめの被演算子は T
の、または T
の曖昧でないアクセス可能な基底クラス B
の、データメンバまたはメンバ関数の名前 (形式的には、それらを表す識別子式) です (E1.E2 や E1->E2 など)。 修飾付きであったり (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
が静的データメンバの場合、
-
E2
が参照型T&
またはT&&
であれば、結果はE2
の参照先のオブジェクトまたは関数を指すT
型の lvalue です。 - そうでなければ、結果はその静的データメンバを指す lvalue です。
- 本質的に、どちらの場合も
E1
は評価され破棄されます。
E2
が非静的データメンバの場合、
-
E2
が参照型T&
またはT&&
であれば、結果はE2
の参照先のオブジェクトまたは関数を指すT
型の lvalue です。 - そうでなく、
E1
が lvalue であれば、結果はE1
のその非静的データメンバを指す lvalue です。 - そうでなければ (
E1
が rvalue (C++17未満)xvalue (prvalue から具体化されたものかもしれません) (C++17以上) であれば)、結果はE1
のその非静的データメンバを指す rvalue (C++11未満)xvalue (C++11以上) です。
-
E2
が mutable なメンバでない場合、結果の cv 修飾はE1
とE2
の cv 修飾の和です。 そうでなければ (E2
が mutable なメンバの場合)、E1
とE2
の volatile 修飾の和です。
E2
が非静的メンバ関数 (デストラクタを含む) の場合、結果はメンバ関数呼び出し演算子の被演算子の左の被演算子としてのみ使用でき他の目的には使用できない、 E1
のその非静的メンバ関数を指す特別な種類の prvalue です。E2
がメンバ列挙子の場合、結果は E1
のそのメンバ列挙子と等しい prvalue です。E1
がスカラー型であり、 E2
が ~
に後続する cv 修飾を除去した同じ型を指す型名 (修飾名でも構いません) または decltype 指定子である場合、結果は関数呼び出し演算子の左側の被演算子としてのみ使用でき他の目的には使用できない、特別な種類の prvalue です。 結果の関数呼び出し式は疑似デストラクタ呼び出しと呼ばれます。 引数を取らず、 void を返し、最初の E1
の評価以外は何も行いません。 これは operator.
の左側の被演算子が非クラス型である唯一のケースです。 疑似デストラクタ呼び出しを許容することにより与えられた型にデストラクタが存在するかどうかを知る必要なくコードを書くことができます。operator.
はオーバーロードできません。 operator->
については、ユーザ定義の演算子に対するオーバーロード解決において、組み込みの演算子はいかなる追加の関数シグネチャも導入しません。 適用可能なオーバーロードされた operator->
が存在する場合、組み込みの operator->
は適用されません。
#include <iostream> 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; // 依存テンプレートメンバのためキーワード template が必要です int* p = T().template ptr<U>(); p->~U(); // U は int であり、 int の疑似デストラクタを呼びます delete p; } }; template<> int A<P>::sn = 2; int main() { A<P> a(1); std::cout << a.n << ' ' << a.sn << ' ' // a.A::sn でも構いません << a.f() << ' ' << a.sf() << ' ' // a.A::sf() でも構いません // << a.B << ' ' // エラー、ネストした型は使用できません << a.RED << ' '; // 列挙子 }
出力:
1 2 11 4 1
[編集] 組み込みのメンバポインタアクセス演算子
メンバへのポインタを通したメンバアクセス演算子式は以下の形式を持ちます。
lhs .* rhs
|
(1) | ||||||||
lhs ->* rhs
|
(2) | ||||||||
T
の式でなければなりません。T*
の式でなければなりません。どちらの演算子も2つめの被演算子は T
のメンバ (データメンバまたはメンバ関数) へのポインタ型または T
の曖昧でないアクセス可能な基底クラス B
のメンバへのポインタ型の式です。
式 E1->*E2 は組み込み型の場合 (*E1).*E2 と正確に同等です。 このため以下のルールでは E1.*E2 についてのみ述べます。
式 E1.*E2 において、
E2
がデータメンバへのポインタの場合、
-
E1
が lvalue であれば、結果はそのデータメンバを指す lvalue です。 - そうでなければ (
E1
が rvalue (C++17未満)xvalue (prvalue から具体化されたものかもしれません) (C++17以上) であれば)、結果はそのデータメンバを指す rvalue (C++11未満)xvalue (C++11以上) です。
E2
がメンバ関数へのポインタの場合、結果はメンバ関数呼び出し演算子の左側の被演算子としてのみ使用でき他の目的には使用できない、メンバ関数を指す特別な種類の prvalue です。E2
がメンバへのヌルポインタ値の場合、動作は未定義です。E1
が rvalue であり、 E2
が ref 修飾子 & を持つメンバ関数を指す場合、そのメンバ関数も const
修飾されているけれども volatile
修飾されていない場合を除いて (C++20以上)プログラムは ill-formed です。E1
が lvalue であり、 E2
が ref 修飾子 && を持つメンバ関数を指す場合、プログラムは ill-formed です。ユーザ定義の演算子に対するオーバーロード解決において、クラス型 B
が D
と同じクラスであるか D
の曖昧でなくアクセス可能な基底クラスであり、 R
がオブジェクト型または関数型であるような、型 D
、 B
、 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; // エラー、 mutable を通して変更することはできません std::cout << s.*pmi << '\n'; D d(7); // 基底クラスのポインタを派生オブジェクトで使用できます 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> のパブリックメンバ関数)
| |
(C++20で非推奨) |
イテレータの指す先の要素にアクセスします ( std::move_iterator<Iter> のパブリックメンバ関数)
|
現在の要素を返します ( std::istream_iterator<T,CharT,Traits,Distance> のパブリックメンバ関数)
| |
何もしません ( std::ostream_iterator<T,CharT,Traits> のパブリックメンバ関数)
| |
(C++11以上)(C++17未満) |
現在の文字のコピーを取得しますCharT がメンバを持つ場合、現在の文字のメンバにアクセスします ( 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 です。 また boost.spirit などの EDSL でも見ることができます。
operator->*
をオーバーロードする標準ライブラリのクラスはありません。 スマートポインタのインタフェースの一部となることが提案され、実際 boost.phoenix のアクタによってその用途で使用されていますが、 cpp.react などの EDSL で使用される方がより一般的です。
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 1213 | C++11 | subscripting an array rvalue resulted in lvalue | reclassified as xvalue |
[編集] 関連項目
一般的な演算子 | ||||||
---|---|---|---|---|---|---|
代入 | インクリメント デクリメント |
算術 | 論理 | 比較 | メンバアクセス | その他 |
a = b |
++a |
+a |
!a |
a == b |
a[b] |
a(...) |
特殊な演算子 | ||||||
static_cast は型を別の関連する型に変換します。 |
メンバアクセス演算子 の C言語リファレンス
|