非静的メンバ関数
非静的メンバ関数は、クラスのメンバ仕様で、staticまたはfriend指定子なしで宣言された関数です (静的メンバ関数およびフレンド宣言でこれらのキーワードの効果を参照)。
class S { int mf1(); // non-static member function declaration void mf2() volatile, mf3() &&; // can have cv-qualifiers and/or a reference-qualifier // the declaration above is equivalent to two separate declarations: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // can be defined inline virtual void mf5() final; // can be virtual, can use final/override S() : data(12) {} // constructors are member functions too int data; }; int S::mf1() { return 7; } // if not defined inline, has to be defined at namespace
コンストラクタ、デストラクタ、および変換関数は、その宣言に特殊な構文を使用します。このページで説明されている規則は、これらの関数には適用されない場合があります。詳細については、それぞれのページを参照してください。
|
明示的オブジェクトメンバ関数は、明示的オブジェクトパラメータを持つ非静的メンバ関数です。 |
(C++23から) |
暗黙的オブジェクトメンバ関数は、明示的オブジェクトパラメータを持たない非静的メンバ関数です (C++23以前は、これのみが非静的メンバ関数であり、文献では「非静的メンバ関数」として言及されていました)。
目次 |
[編集] 説明
あらゆる関数宣言が許可され、非静的メンバ関数にのみ利用可能な追加の構文要素があります: 純粋指定子、cv修飾子、ref修飾子、finalおよびoverride指定子(C++11以降)、およびメンバ初期化リスト。
クラスXの非静的メンバ関数は、次のように呼び出すことができます。
Xのオブジェクトに対してXのメンバ関数の本体内から直接Xから派生したクラスのメンバ関数の本体内から直接型Xではないオブジェクト、またはXから派生した型ではないオブジェクトに対してクラスXの非静的メンバ関数を呼び出すと、未定義の動作が発生します。
Xの非静的メンバ関数の本体内で、Xの非型非静的メンバまたはXの基底クラスの非型非静的メンバに解決されるid-expression e (例: 識別子) は、メンバアクセス式 (*this).e に変換されます (すでにメンバアクセス式の一部である場合を除く)。これはテンプレート定義コンテキストでは発生しないため、依存させるために明示的にthis->をプレフィックスとして付ける必要がある場合があります。
struct S { int n; void f(); }; void S::f() { n = 1; // transformed to (*this).n = 1; } int main() { S s1, s2; s1.f(); // changes s1.n }
Xの非静的メンバ関数の本体内で、XまたはXの基底クラスの静的メンバ、列挙子、または入れ子型に解決される修飾なしIDは、対応する修飾IDに変換されます。
struct S { static int n; void f(); }; void S::f() { n = 1; // transformed to S::n = 1; } int main() { S s1, s2; s1.f(); // changes S::n }
[編集] cv修飾子付きメンバ関数
暗黙的オブジェクトメンバ関数は、cv修飾子シーケンス (const、volatile、またはconstとvolatileの組み合わせ) を付けて宣言できます。このシーケンスは、関数宣言のパラメータリストの後に記述されます。異なるcv修飾子シーケンス (またはシーケンスなし) を持つ関数は異なる型を持ち、互いにオーバーロードできます。
cv修飾子シーケンスを持つ関数の本体では、*thisはcv修飾されます。たとえば、const修飾子を持つメンバ関数では、const修飾子を持つ他のメンバ関数のみが通常どおり呼び出されます。const修飾子を持たないメンバ関数は、const_castが適用される場合、またはthisを含まないアクセスパスを介して呼び出すことができます。
#include <vector> struct Array { std::vector<int> data; Array(int sz) : data(sz) {} // const member function int operator[](int idx) const { // the this pointer has type const Array* return data[idx]; // transformed to (*this).data[idx]; } // non-const member function int& operator[](int idx) { // the this pointer has type Array* return data[idx]; // transformed to (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK: the type of a[1] is int& const Array ca(10); ca[1] = 2; // Error: the type of ca[1] is int }
ref修飾子付きメンバ関数暗黙的オブジェクトメンバ関数は、ref修飾子なし、lvalue ref修飾子 (パラメータリストの後のトークン
注: cv修飾とは異なり、ref修飾は |
(C++11以降) |
[編集] 仮想関数および純粋仮想関数
非静的メンバ関数は仮想または純粋仮想として宣言できます。詳細については、仮想関数および抽象クラスを参照してください。
明示的オブジェクトメンバ関数cv修飾子またはref修飾子なしで宣言された非静的非仮想メンバ関数について、その最初のパラメータが関数パラメータパックでない場合、thisキーワードをプレフィックスとして付けた明示的オブジェクトパラメータとすることができます。 struct X { void foo(this X const& self, int i); // same as void foo(int i) const &; // void foo(int i) const &; // Error: already declared void bar(this X self, int i); // pass object by value: makes a copy of “*this” }; メンバ関数テンプレートの場合、明示的オブジェクトパラメータは型と値カテゴリの推論を可能にします。この言語機能は「thisの推論」と呼ばれます。 struct X { template<typename Self> void foo(this Self&&, int); }; struct D : X {}; void ex(X& x, D& d) { x.foo(1); // Self = X& move(x).foo(2); // Self = X d.foo(3); // Self = D& } これにより、constおよび非constメンバ関数の重複を排除できます。例については、配列添字演算子を参照してください。 明示的オブジェクトメンバ関数の本体内では、thisポインタは使用できません。すべてのメンバアクセスは、静的メンバ関数と同様に、最初のパラメータを介して行う必要があります。 struct C { void bar(); void foo(this C c) { auto x = this; // error: no this bar(); // error: no implicit this-> c.bar(); // ok } }; 明示的オブジェクトメンバ関数へのポインタは、通常の関数へのポインタであり、メンバへのポインタではありません。 struct Y { int f(int, int) const&; int g(this Y const&, int, int); }; auto pf = &Y::f; pf(y, 1, 2); // error: pointers to member functions are not callable (y.*pf)(1, 2); // ok std::invoke(pf, y, 1, 2); // ok auto pg = &Y::g; pg(y, 3, 4); // ok (y.*pg)(3, 4); // error: “pg” is not a pointer to member function std::invoke(pg, y, 3, 4); // ok |
(C++23から) |
[編集] 特殊メンバ関数
一部のメンバ関数は特殊です。特定の状況下では、ユーザによって定義されていなくても、コンパイラによって定義されます。それらは次のとおりです。
| (C++11以降) |
| (C++11以降) |
- デストラクタ(C++20まで)プロスペクティブデストラクタ(C++20以降)
特殊メンバ関数は、比較演算子とともに(C++20以降)、デフォルト化できる唯一の関数です。つまり、関数本体の代わりに= defaultを使用して定義できます (詳細はそれぞれのページを参照)。
[編集] 注釈
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_ref_qualifiers |
200710L |
(C++11) | ref 修飾子 |
__cpp_explicit_this_parameter |
202110L |
(C++23) | 明示的オブジェクトパラメータ (thisの推論) |
[編集] 例
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // simple converting constructor (declaration) S(int val); // simple explicit constructor (declaration) explicit S(std::string str); // const member function (definition) virtual int getData() const { return data; } }; // definition of the constructor S::S(int val) : data(val) { std::cout << "ctor1 called, data = " << data << '\n'; } // this constructor has a catch clause S::S(std::string str) try : data(std::stoi(str)) { std::cout << "ctor2 called, data = " << data << '\n'; } catch(const std::exception&) { std::cout << "ctor2 failed, string was '" << str << "'\n"; throw; // ctor's catch clause should always rethrow } struct D : S { int data2; // constructor with a default argument D(int v1, int v2 = 11) : S(v1), data2(v2) {} // virtual member function int getData() const override { return data * data2; } // lvalue-only assignment operator D& operator=(D other) & { std::swap(other.data, data); std::swap(other.data2, data2); return *this; } }; int main() { D d1 = 1; S s2("2"); try { S s3("not a number"); } catch(const std::exception&) {} std::cout << s2.getData() << '\n'; D d2(3, 4); d2 = d1; // OK: assignment to lvalue // D(5) = d1; // ERROR: no suitable overload of operator= }
出力
ctor1 called, data = 1 ctor2 called, data = 2 ctor2 failed, string was 'not a number' 2 ctor1 called, data = 3
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 194 | C++98 | 非静的メンバ関数が 囲むクラス名と同じ名前を持つことができるかどうか曖昧 |
明示的な名前の制限が追加されました |