メンバーアクセス演算子
オペランドのメンバにアクセスします。
| 演算子 名前 | 構文 | オーバーロード可能 | プロトタイプの例 (class T の場合) | |
|---|---|---|---|---|
| クラス定義内 | クラス定義外 | |||
| 添え字 | a[b] | はい | R& T::operator[](S b); | N/A |
| a[...] (C++23以降) | R& T::operator[](...); | |||
| 間接参照 | *a | はい | R& T::operator*(); | R& operator*(T a); |
| アドレス取得 | &a | はい | R* T::operator&(); | R* operator&(T a); |
| オブジェクトのメンバ | a.b | いいえ | N/A | N/A |
| ポインタのメンバ | a->b | はい | R* T::operator->(); | N/A |
| オブジェクトへのポインタのメンバ | a.*b | いいえ | N/A | N/A |
| ポインタへのポインタのメンバ | 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から) | |||||||
組み込み添え字式 E1[E2] は、値カテゴリ(下記参照)を除き、式 *(E1 + E2) と完全に同一です(および評価順序(C++17以降))。ポインタオペランド(配列からポインタへの変換の結果である可能性があり、配列の要素またはその次の要素を指している必要があります)は、ポインタ演算の規則に従って、同じ配列の別の要素を指すように調整され、その後逆参照されます。
配列に適用された場合、添え字式は、配列がlvalueであればlvalueであり、そうでない場合はxvalueとなります((C++11以降)(since C++11))。
ポインタに適用された場合、添え字式は常にlvalueです。
型Tは不完全型であってはいけません。たとえTのサイズや内部構造が使用されない場合でも(例:&x[0])。
|
添え字演算子の2番目(右側)の引数として括弧で囲まれていないカンマ式を使用することは非推奨です。 例えば、a[b, c] は非推奨ですが、a[(b, c)] はそうではありません。 |
(C++20以降) (C++23まで) |
|
添え字演算子の2番目(右側)の引数として、括弧で囲まれていないカンマ式を使用することはできません。例えば、a[b, c] は、ill-formed であるか、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 が指すオブジェクトまたは関数を参照するlvalueです。expr が実際にオブジェクトまたは関数を指していない場合、未定義の動作となります(typeid で指定された場合を除く)。
(おそらくcv修飾された)void へのポインタは逆参照できません。他の不完全型へのポインタは逆参照できますが、結果のlvalueは、lvalueが不完全型を許容するコンテキスト(例えば、参照の初期化時)でのみ使用できます。
ユーザー定義演算子へのオーバーロード解決において、オブジェクト型(cv修飾されている可能性あり)または関数型(const または参照修飾されていない)のすべての型 `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) | ||||||||
|
もしexprが明示的オブジェクトメンバ関数の名前である場合、exprは修飾識別子でなければなりません。明示的オブジェクトメンバ関数を指す修飾されていない識別子に`&`を適用するとill-formedになります。 |
(C++23から) |
ユーザー定義演算子へのオーバーロード解決において、この演算子は追加の関数シグネチャを導入しません。組み込みアドレス取得演算子は、実行可能な関数であるオーバーロードされた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 .pseudo-destructor |
(3) | ||||||||
expr ->pseudo-destructor |
(4) | ||||||||
id-expr は、`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 が参照型 `T&` (または `T&&`(C++11以降))である場合、結果は参照が束縛されているオブジェクトまたは関数を指定する型 `T` のlvalueになります。
- それ以外の場合、E2 の型が `T` であるとすると、結果は、その静的データメンバを指定する型 `T` のlvalueになります。
- もし E2 が参照型 `T&` (または `T&&`(C++11以降))である場合、結果は E1 の対応する参照メンバが束縛されているオブジェクトまたは関数を指定する型 `T` のlvalueになります。
- それ以外の場合(E1 がlvalueである場合)、結果は E1 のその非静的データメンバを指定するlvalueになります。
- それ以外の場合(E1 がrvalue(C++17まで)xvalue(一時化されたprvalueである可能性あり)(C++17以降))、結果は E1 のその非静的データメンバを指定するrvalue(C++11まで)xvalue(C++11以降)になります。
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
E1 の結果が、E1 の型と類似していないオブジェクトの型であり、かつ E2 が非静的メンバである場合、動作は未定義です。
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) | ||||||||
rhs は、`T` または `T` の明示的かつアクセス可能な基底クラス `B` のメンバへのポインタ(データまたは関数)のrvalueでなければなりません。
式 E1->*E2 は、組み込み型の場合、式 (*E1).*E2 と完全に等価です。そのため、以下の規則は E1.*E2 のみを対象としています。
式 E1.*E2 では
- もし E1 がlvalueである場合、結果はそのデータメンバを指定するlvalueになります。
- それ以外の場合(E1 がrvalue(C++17まで)xvalue(一時化されたprvalueである可能性あり)(C++17以降))、結果はrvalue(C++11まで)xvalue(C++11以降) で、そのデータメンバを指定します。
|
7) E1 がlvalueであり、E2 がref修飾子 `&&` を持つメンバ関数を指す場合、プログラムはill-formedです。
|
(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> の public member function) | |
| 管理下の配列へのインデックスアクセスを提供します。 ( std::unique_ptr<T,Deleter> の public member function) | |
| 指定された文字にアクセスする ( std::basic_string<CharT,Traits,Allocator> の public member function) | |
| 指定された要素にアクセスする ( std::array<T,N> の public member function) | |
| 指定された要素にアクセスする ( std::deque<T,Allocator> の public member function) | |
| 指定された要素にアクセスする ( std::vector<T,Allocator> の public member function) | |
| 指定された要素にアクセスまたは挿入する ( std::map<Key,T,Compare,Allocator> の public member function) | |
| 指定された要素にアクセスまたは挿入する ( std::unordered_map<Key,T,Hash,KeyEqual,Allocator> の public member function) | |
| インデックスで要素にアクセスする ( std::reverse_iterator<Iter> の public member function) | |
| インデックスで要素にアクセスする ( std::move_iterator<Iter> の public member function) | |
| valarrayの要素、スライス、またはマスクを取得/設定する ( std::valarray<T> の public member function) | |
| 指定されたサブマッチを返します。 ( std::match_results<BidirIt,Alloc> の public member function) |
間接参照演算子およびメンバ演算子は、多くのイテレータおよびスマートポインタクラスによってオーバーロードされています。
| 管理対象オブジェクトへのポインタを間接参照する ( std::unique_ptr<T,Deleter> の public member function) | |
| 格納されたポインターを間接参照する ( std::shared_ptr<T> の public member function) | |
| 管理対象オブジェクトにアクセスする ( std::auto_ptr<T> の public member function) | |
| イテレータを逆参照します。 ( std::raw_storage_iterator<OutputIt,T> の public member function) | |
| デクリメントされた基底イテレータの現在の要素を返します。 ( std::reverse_iterator<Iter> の public member function) | |
| 何も行わない ( std::back_insert_iterator<Container> の public member function) | |
| 何も行わない ( std::front_insert_iterator<Container> の public member function) | |
| 何も行わない ( std::insert_iterator<Container> の public member function) | |
| 指し示す要素にアクセスする ( std::move_iterator<Iter> の public member function) | |
| 現在の要素にアクセスします。 ( std::istream_iterator<T,CharT,Traits,Distance> の public member function) | |
| 何も行わない ( std::ostream_iterator<T,CharT,Traits> の public member function) | |
| 現在の文字のコピーを取得します。 ( std::istreambuf_iterator<CharT,Traits> の public member function) | |
| 何も行わない ( std::ostreambuf_iterator<CharT,Traits> の public member function) | |
| 現在のマッチにアクセスします。 ( std::regex_iterator<BidirIt,CharT,Traits> の public member function) | |
| 現在のサブマッチにアクセスします。 ( std::regex_token_iterator<BidirIt,CharT,Traits> の public member function) |
標準ライブラリのクラスで operator& をオーバーロードしているものはありません。最もよく知られている operator& のオーバーロード例は Microsoft の COM クラス CComPtr ですが、boost.spirit のような EDSLs にも現れることがあります。
標準ライブラリのクラスで operator->* をオーバーロードしているものはありません。スマートポインタインターフェースの一部として提案されたこともあり、実際、スマートポインタインターフェースでそのように使用されており、boost.phoenix のアクターによって使用されていますが、cpp.react のような EDSLs ではより一般的です。
[編集] 注釈
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_multidimensional_subscript |
202110L |
(C++23) | 多次元添え字演算子 |
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1213 | C++11 | 配列rvalueの添え字演算がlvalueになった | xvalueとして再分類 |
| CWG 1458 | C++98 | 不完全クラス型のlvalueに`&`を適用し operator& を宣言した場合、未定義の動作になった |
未指定 どちらの&が使用されるか |
| CWG 1642 | C++98 | 組み込みポインタメンバーアクセス演算子における rhs はlvalueになりうる | rvalueのみになりうる |
| CWG 1800 | C++98 | メンバ無名共用体の 非静的データメンバに`&`を適用した場合、 無名共用体が結果型に関与するかどうか不明確だった |
無名共用体は 含まれない 結果型に |
| CWG 2614 | C++98 | E1.E2 の結果は、E2 が参照メンバまたは列挙子である場合に不明確だった | 明確化された |
| CWG 2725 | C++98 | E2 が静的メンバ関数である場合、E1.E2 は、 operator() の左辺オペランドでなくても、well-formed であった |
E1.E2 はill-formed この場合に |
| CWG 2748 | C++98 | E1 が nullポインタであり、E2 が静的メンバを参照する場合の E1->E2 の動作は不明確だった |
この場合の動作は 未定義となる |
| CWG 2813 | C++98 | E1 は破棄値式ではなかった、もし E1.E2 が静的メンバまたは列挙子を名前としていた場合 |
そうである |
| CWG 2823 | C++98 | *expr の動作は、expr が オブジェクトまたは関数を指していなかった場合、不明確だった |
明確化された |
[編集] 関連項目
| 共通の演算子 | ||||||
|---|---|---|---|---|---|---|
| 代入 | インクリメント デクリメント |
算術 | 論理 | 比較 | メンバ アクセス |
その他 |
|
a = b |
++a |
+a |
!a |
a == b |
a[...] |
関数呼び出し a(...) |
| コンマ a, b | ||||||
| conditional a ? b : c | ||||||
| 特殊な演算子 | ||||||
|
static_castは、ある型を関連する別の型に変換する | ||||||
| C のドキュメント (メンバアクセス演算子)
|