Function declaration
関数宣言は関数名とその型を導入します。関数定義は関数名/型と関数本体を関連付けます。
目次 |
関数宣言
関数宣言は任意のスコープに現れることができます。クラススコープでの関数宣言は( friend 指定子が使用されない限り)クラスメンバ関数を導入します。詳細については メンバ関数 および フレンド関数 を参照してください。
noptr-declarator
(
parameter-list
)
cv
(オプション)
ref
(オプション)
except
(オプション)
attr
(オプション)
|
(1) | ||||||||
noptr-declarator
(
parameter-list
)
cv
(オプション)
ref
(オプション)
except
(オプション)
attr
(オプション)
->
trailing
|
(2) | (C++11以降) | |||||||
(詳細は Declarations を参照。他の形式の declarator 構文については)
| noptr-declarator | - |
任意の有効な
declarator
ですが、
*
、
&
、または
&&
で始まる場合は括弧で囲む必要があります。
|
||||||
| parameter-list | - | 空である可能性のある、カンマ区切りの関数パラメータのリスト(詳細は下記参照) | ||||||
| attr | - | (C++11以降) 属性 のリスト。これらの属性は関数自体ではなく、関数の型に適用されます。関数の属性は、識別子の後ろの宣言子内に現れ、宣言の先頭に現れる属性(もしあれば)と結合されます。 | ||||||
| cv | - | const/volatile修飾。非静的メンバ関数宣言でのみ許可されます | ||||||
| ref | - | (C++11以降) 参照修飾。非静的メンバ関数宣言でのみ許可されます | ||||||
| except | - |
|
||||||
| trailing | - | 後置戻り値型。戻り値の型が引数名に依存する場合(例: template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; )や複雑な場合(例: auto fpif ( int ) - > int ( * ) ( int ) )に有用です |
|
宣言 で述べたように、宣言子の後には requires 節 を続けることができ、これは関数の関連する 制約 を宣言します。この制約は、 オーバーロード解決 によって関数が選択されるために満たされなければなりません。(例: void f1 ( int a ) requires true ; )関連する制約は関数のシグネチャの一部ですが、関数型の一部ではないことに注意してください。 |
(C++20以降) |
関数宣言子は、他の宣言子と混在させることができます。ただし、 宣言指定子シーケンス が許可する場合に限ります:
// int、int*、関数、関数ポインタを宣言 int a = 1, *p = NULL, f(), (*pf)(double); // decl-specifier-seq は int // 宣言子 f() は引数を取らずintを返す関数を宣言(定義ではない) struct S { virtual int f(char) const, g(int) &&; // 2つの非静的メンバ関数を宣言 virtual int f(char), x; // コンパイルエラー: virtual(decl-specifier-seq内)は // 非静的メンバ関数の宣言でのみ許可される };
|
volatile修飾されたオブジェクト型をパラメータ型または戻り値の型として使用することは非推奨です。 |
(since C++20) |
関数の戻り値の型は、関数型または配列型にすることはできません(ただし、それらへのポインタまたは参照は可能です)。
|
あらゆる宣言と同様に、宣言の前に現れる属性と、宣言子内の識別子の直後に現れる属性は両方とも、宣言または定義されているエンティティ(この場合は関数)に適用されます: [[noreturn]] void f [[noreturn]] (); // OK: 両方の属性が関数fに適用される しかしながら、宣言子の後に現れる属性(上記の構文では)は、関数自体ではなく関数の型に適用されます: void f() [[noreturn]]; // エラー: この属性は関数自体に効果を持たない |
(C++11以降) |
戻り値型の推論関数宣言の decl-specifier-seq にキーワード auto が含まれている場合、後置戻り値型は省略可能であり、コンパイラによって 非破棄 return 文で使用されるオペランドの型から推論されます。戻り値型が decltype ( auto ) を使用しない場合、推論は テンプレート引数推論 の規則に従います: int x = 1; auto f() { return x; } // 戻り値の型は int const auto& f() { return x; } // 戻り値の型は const int&
戻り値の型が
decltype
(
auto
)
の場合、戻り値の型はreturn文で使用されたオペランドが
int x = 1; decltype(auto) f() { return x; } // 戻り値の型はint、decltype(x)と同じ decltype(auto) f() { return(x); } // 戻り値の型はint&、decltype((x))と同じ (注:「 const decltype ( auto ) & 」は誤りです。 decltype ( auto ) は単独で使用する必要があります) 複数のreturn文がある場合、それらはすべて同じ型に推論されなければなりません: auto f(bool val) { if (val) return 123; // 戻り値の型をintと推論 else return 3.14f; // エラー: 戻り値の型をfloatと推論 } return文がない場合、またはreturn文の被演算子がvoid式(被演算子のないreturn文を含む)である場合、宣言された戻り値の型は decltype ( auto ) でなければならず、この場合の推定される戻り値の型は void であるか、あるいは(possibly cv-qualified) auto でなければならず、この場合の推定される戻り値の型は(同一のcv修飾を持つ) void となる: auto f() {} // voidを返す auto g() { return f(); } // voidを返す auto* x() {} // エラー: voidからauto*を推論できません 関数内でreturn文が一度見つかると、その文から推測された戻り値の型が関数の残りの部分、他のreturn文を含めて使用されるようになります: auto sum(int i) { if (i == 1) return i; // sumの戻り値型はint else return sum(i - 1) + i; // OK: sumの戻り値型は既に判明している } 戻り値の文が 波括弧で囲まれた初期化子リスト を使用する場合、推論は許可されません: auto func() { return {1, 2, 3}; } // エラー 仮想関数 および コルーチン (C++20以降) は戻り値型の推論を使用できません: struct F { virtual auto f() { return 2; } // エラー }; 関数テンプレート のうち、 ユーザー定義変換関数 以外は戻り値型の推論を使用できます。この推論は、return文内の式が 依存名 でない場合でも、インスタンス化時に発生します。このインスタンス化は、 SFINAE の目的において即時コンテキストには含まれません。 template<class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; // f<int>をインスタンス化して戻り値の型を推論 template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } // 両方のfをインスタンス化して戻り値の型を決定し、 // 2番目のテンプレートオーバーロードを選択 戻り値の型推論を使用する関数または関数テンプレートの再宣言または特殊化は、同じ戻り値のプレースホルダーを使用する必要があります: auto f(int num) { return num; } // int f(int num); // エラー: プレースホルダー戻り値型なし // decltype(auto) f(int num); // エラー: 異なるプレースホルダー template<typename T> auto g(T t) { return t; } template auto g(int); // OK: 戻り値型はint // template char g(char); // エラー: プライマリテンプレートgの特殊化ではない 同様に、戻り値型の推論を使用しない関数または関数テンプレートの再宣言または特殊化は、プレースホルダーを使用してはなりません: int f(int num); // auto f(int num) { return num; } // エラー: fの再宣言ではない template<typename T> T g(T t) { return t; } template int g(int); // OK: Tをintとして特殊化 // template auto g(char); // エラー: プライマリテンプレートgの特殊化ではない 明示的インスタンス化宣言 は、戻り値型推論を使用する関数テンプレート自体をインスタンス化しません: template<typename T> auto f(T t) { return t; } extern template auto f(int); // f<int>のインスタンス化は行わない int (*p)(int) = f; // 戻り値の型を決定するためにf<int>をインスタンス化するが、 // 明示的なインスタンス化定義はプログラム内のどこかで // 依然として必要である |
(C++14以降) |
パラメータリスト
パラメータリストは、関数が呼び出された際に指定できる引数を決定します。これはカンマ区切りの parameter declarations のリストであり、それぞれ以下の構文を持ちます:
| attr (オプション) decl-specifier-seq declarator | (1) | ||||||||
|
attr
(オプション)
|
(2) | (C++23以降) | |||||||
attr
(オプション)
decl-specifier-seq
declarator
=
initializer
|
(3) | ||||||||
| attr (オプション) decl-specifier-seq abstract-declarator (オプション) | (4) | ||||||||
|
attr
(オプション)
|
(5) | (C++23以降) | |||||||
attr
(オプション)
decl-specifier-seq
abstract-declarator
(オプション)
=
initializer
|
(6) | ||||||||
void
|
(7) | ||||||||
| 誤った使用法 | 例 |
|---|---|
| 複数のパラメータが存在する場合 | int f1 ( void , int ) ; |
| void パラメータが名前付けされている場合 | inf f2 ( void param ) ; |
| void がCV修飾されている場合 | int f3 ( const void ) ; |
| void が 依存名 である場合 |
int
f4
(
T
)
;
(ただし
T
が
void
の場合)
|
| void パラメータが 明示的オブジェクトパラメータ である場合 (C++23以降) | int f5 ( this void ) ; |
|
decl-specifier-seq は型指定子以外の specifiers が存在しうることを示唆しているが、実際に許可されている他の指定子は register および auto (C++11まで) のみであり、効果はない。 |
(C++17まで) |
|
関数パラメータのいずれかが プレースホルダー ( auto または concept type )を使用する場合、その関数宣言は 省略形関数テンプレート 宣言となる: void f1(auto); // same as template<class T> void f1(T) void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept |
(C++20以降) |
|
指定子 this を持つパラメータ宣言(構文 ( 2 ) / ( 5 ) )は 明示的オブジェクトパラメータ を宣言する。 明示的オブジェクトパラメータは function parameter pack にはなれず、以下の宣言のパラメータリストの最初のパラメータとしてのみ現れることができる:
明示的オブジェクトパラメータを持つmember functionには以下の制限がある: struct C { void f(this C& self); // OK template<typename Self> void g(this Self&& self); // also OK for templates void p(this C) const; // Error: “const” not allowed here static void q(this C); // Error: “static” not allowed here void r(int, this C); // Error: an explicit object parameter // can only be the first parameter }; // void func(this C& self); // Error: non-member functions cannot have // an explicit object parameter |
(C++23以降) |
関数宣言で宣言されたパラメータ名は、通常は自己文書化の目的のみに使用されます。それらは関数定義で使用されますが(任意のままです)。
パラメータリストにおいて、型名が括弧内にネストされている場合に曖昧性が生じます ( lambda expressions を含む) (C++11以降) 。この場合、関数ポインタ型のパラメータ宣言と、 declarator の識別子を冗長な括弧で囲んだパラメータ宣言の間で選択が生じます。解決策は、その型名を simple type specifier (関数ポインタ型)として扱うことです:
class C {}; void f(int(C)) {} // void f(int(*fp)(C param)) {} // これは void f(int C) {} ではない void g(int *(C[10])); // void g(int *(*fp)(C param[10])); // これは void g(int *C[10]); ではない
パラメータの型は、未知の境界を持つ配列への参照またはポインタを含む型(そのような型の多段階ポインタ/配列を含む)、またはパラメータがそのような型である関数へのポインタを含むことはできません。
省略記号の使用
パラメータリストの最後のパラメータは省略記号( ... )にすることができます。これは 可変引数関数 を宣言します。省略記号の前のカンマは省略可能です (C++26で非推奨) :
int printf(const char* fmt, ...); // 可変引数関数 int printf(const char* fmt...); // 上記と同じだが、C++26以降非推奨 template<typename... Args> void f(Args..., ...); // パラメータパックを持つ可変引数関数テンプレート template<typename... Args> void f(Args... ...); // 上記と同じだが、C++26以降非推奨 template<typename... Args> void f(Args......); // 上記と同じだが、C++26以降非推奨
関数型
パラメータ型リスト
関数の parameter-type-list は以下のように決定されます:
- 各パラメータの型 (関数 parameter packs を含む) (since C++11) は、それぞれの parameter declaration から決定されます。
-
各パラメータの型を決定した後、「
Tの配列」型または関数型Tのパラメータは「Tへのポインタ」に調整されます。 - パラメータ型のリストを生成した後、関数型を形成する際にパラメータ型を修飾するトップレベルの cv-qualifiers は削除されます。
- 変換されたパラメータ型の結果リストと、 ellipsis または関数 parameter pack (since C++11) の有無が、関数のparameter-type-listとなります。
void f(char*); // #1 void f(char[]) {} // #1を定義 void f(const char*) {} // OK、別のオーバーロード void f(char* const) {} // エラー: #1を再定義 void g(char(*)[2]); // #2 void g(char[3][2]) {} // #2を定義 void g(char[3][3]) {} // OK、別のオーバーロード void h(int x(const int)); // #3 void h(int (*)(int)) {} // #3を定義
関数型の決定
構文
(1)
において、
noptr-declarator
を独立した宣言と仮定し、
noptr-declarator
内の
qualified-id
または
unqualified-id
の型が「derived-declarator-type-list
T
」で与えられる場合:
|
(C++17以降) |
-
それ以外の場合、
(C++17まで)
それ以外の場合、
(C++17以降)
宣言された関数の型は
「derived-declarator-type-list function of
parameter-type-list cv (オプション) ref (オプション) (C++11以降) returningT」となる。
|
構文
(2)
において、
noptr-declarator
を独立した宣言と仮定し、
noptr-declarator
内の
qualified-id
または
unqualified-id
の型が「derived-declarator-type-list
|
(C++11以降) |
|
(C++17以降) |
attr が存在する場合、それは関数型に適用される。 |
(C++11以降) |
// 「f1」の型は // 「intを引数に取りvoidを返す関数、noreturn属性付き」 void f1(int a) [[noreturn]]; // 「f2」の型は // 「constexpr noexcept関数、intへのポインタを引数に取りintを返す」 constexpr auto f2(int[] b) noexcept -> int; struct X { // 「f3」の型は // 「引数なしconst関数、const intを返す」 const int f3() const; };
トレーリング修飾子
関数型に
cv
または
ref
(C++11以降)
修飾子(
typedef
名で指定される型を含む)が付いたものは、以下の場合にのみ現れます:
- 非静的メンバ関数の関数型、
- メンバポインタが参照する関数型、
- 関数のtypedef宣言 typedef のトップレベル関数型 または エイリアス宣言 (C++11以降) 、
- テンプレート型パラメータのデフォルト引数における type-id 、または
- テンプレート型パラメータに対するテンプレート引数のtype-id。
typedef int FIC(int) const; FIC f; // エラー: メンバー関数を宣言していない struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK
関数シグネチャ
すべての関数にはシグネチャがあります。
関数のシグネチャは、その名前と parameter-type-list から構成されます。そのシグネチャには、以下の例外を除いて、外側の namespace も含まれます:
- 関数が メンバ関数 である場合、そのシグネチャには、囲んでいる名前空間の代わりに、その関数がメンバであるクラスが含まれます。また、存在する場合は以下の構成要素もシグネチャに含まれます:
-
- cv
|
(C++11以降) |
|
(C++20以降) |
except および attr (C++11以降) は関数シグネチャに関与しない 、ただし noexcept 指定 は関数型に影響する (C++17以降) 。
関数定義
非メンバ関数の定義は名前空間スコープでのみ記述可能です(ネストした関数は存在しません)。 member function の定義は class definition の本体内部でも記述できます。これらは以下の構文を持ちます:
|
attr
(オプション)
decl-specifier-seq
(オプション)
declarator
virt-specs (オプション) contract-specs (オプション) function-body |
(1) | ||||||||
|
attr
(オプション)
decl-specifier-seq
(オプション)
declarator
requires-clause contract-specs (オプション) function-body |
(2) | (C++20以降) | |||||||
| attr | - | (C++11以降) 属性 のリスト。これらの属性は、識別子の後の declarator 内の属性(このページの上部を参照)と結合されます(もしあれば)。 |
| decl-specifier-seq | - | 宣言文法 と同様の、指定子付きの戻り値型 |
| declarator | - | 関数宣言子。上記の関数宣言文法と同じ(括弧で囲むことが可能) |
| virt-specs | - |
(C++11以降)
override
、
final
、またはそれらの任意の順序での組み合わせ
|
| requires-clause | - | requires 節 |
| contract-specs | - | (C++26以降) 関数契約指定子 のリスト |
| function-body | - | 関数本体(下記参照) |
function-body
は以下のいずれかです:
| コンストラクタ初期化子 (オプション) 複合文 | (1) | ||||||||
| 関数tryブロック | (2) | ||||||||
=
default
;
|
(3) | (C++11以降) | |||||||
=
delete
;
|
(4) | (C++11以降) | |||||||
=
delete
(
文字列リテラル
);
|
(5) | (C++26以降) | |||||||
| ctor-initializer | - | メンバ初期化子リスト 、コンストラクタでのみ許可される |
| compound-statement | - | 関数の本体を構成する中括弧で囲まれた 文のシーケンス |
| function-try-block | - | 関数の try ブロック |
| string-literal | - | 関数が削除された理由を説明するために使用できる 未評価文字列リテラル |
int max(int a, int b, int c) { int m = (a > b) ? a : b; return (m > c) ? m : c; } // decl-specifier-seq は「int」 // declarator は「max(int a, int b, int c)」 // body は { ... }
関数本体は compound statement (一対の波括弧で囲まれたゼロ個以上の文のシーケンス)であり、関数呼び出しが行われたときに実行されます。さらに、 constructor の関数本体には以下も含まれます:
- コンストラクタの メンバ初期化子リスト に識別子が存在しないすべての非静的データメンバについて、 デフォルトメンバ初期化子 または (C++11以降) デフォルト初期化 が対応するメンバ サブオブジェクト の初期化に使用される。
- コンストラクタのメンバ初期化子リストに型名が存在しないすべての基底クラスについて、デフォルト初期化が対応する基底クラスサブオブジェクトの初期化に使用される。
|
関数定義が virt-specs を含む場合、それは メンバー関数 を定義しなければならない。 |
(C++11以降) |
|
関数定義が requires-clause を含む場合、それは テンプレート化された関数 を定義しなければならない。 |
(C++20以降) |
void f() override {} // エラー: メンバー関数ではありません void g() requires (sizeof(int) == 4) {} // エラー: テンプレート関数ではありません
関数定義のパラメータ型および戻り値の型は、(cv修飾されている可能性のある) incomplete class types であってはならない (C++11以降) 。ただし、関数がdeletedとして定義されている場合は除く。完全性チェックは関数本体でのみ行われるため、 member functions は、定義時点では不完全であっても(関数本体では完全である)、自身が定義されているクラス(またはその外側のクラス)を返すことができる。
関数定義の declarator で宣言されたパラメータは、本体内部で in scope となります。関数本体でパラメータが使用されない場合、名前を付ける必要はありません(抽象宣言子を使用すれば十分です):
void print(int a, int) // 2番目のパラメータは使用されない { std::printf("a = %d\n", a); }
仮引数のトップレベルの cv修飾子 は関数宣言では無視されますが、関数本体内部で見えるパラメータの型を変更します:
void f(const int n) // void(int)型の関数を宣言 { // ただし、本体内部では「n」の型はconst int }
デフォルト化関数関数定義が構文 ( 3 ) の場合、その関数は 明示的にデフォルト化された ものとして定義されます。 明示的にデフォルト化された関数は、 特殊メンバ関数 または 比較演算子関数 (C++20以降) でなければならず、 デフォルト引数 を持ってはならない。
明示的にデフォルト化された特殊メンバ関数
最初の宣言で明示的にデフォルト化された関数は、暗黙的に inline となり、また constexpr function として可能な場合には暗黙的にconstexprとなります。 struct S { S(int a = 0) = default; // エラー: デフォルト引数 void operator=(const S&) = default; // エラー: 戻り値の型が一致しない ~S() noexcept(false) = default; // OK、例外仕様が異なる private: int i; S(S&); // OK、プライベートなコピーコンストラクタ }; S::S(S&) = default; // OK、コピーコンストラクタを定義 明示的にデフォルト化された関数と暗黙的に宣言された関数は、まとめて デフォルト化された 関数と呼ばれます。それらの実際の定義は暗黙的に提供されます。詳細については、それぞれの対応するページを参照してください。 削除された関数関数定義が構文 ( 4 ) または ( 5 ) (C++26以降) の場合、その関数は 明示的に削除された と定義されます。 削除された関数のあらゆる使用は不適格です(プログラムはコンパイルされません)。これには、明示的な呼び出し(関数呼び出し演算子を使用)と暗黙的な呼び出し(削除されたオーバーロード演算子、特殊メンバ関数、アロケーション関数などの呼び出し)、削除された関数へのポインタまたはメンバへのポインタの構築、および potentially-evaluated ではない式における削除された関数の使用も含まれます。 非純粋仮想メンバー関数は、暗黙的に odr-used されている場合でも、deletedとして定義することができます。deleted関数はdeleted関数によってのみオーバーライドでき、非deleted関数は非deleted関数によってのみオーバーライドできます。
関数がオーバーロードされている場合、 オーバーロード解決 が最初に行われ、削除された関数が選択された場合にのみプログラムは不適格となります: struct T { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete("new[] is deleted"); // C++26以降 }; T* p = new T; // エラー:削除されたT::operator newの呼び出しを試みる T* p = new T[5]; // エラー:削除されたT::operator new[]の呼び出しを試みる // 診断メッセージ「new[] is deleted」を出力 関数の削除定義は、翻訳単位内で最初の宣言でなければなりません:以前に宣言された関数を削除済みとして再宣言することはできません: struct T { T(); }; T::T() = delete; // エラー: 最初の宣言で削除されなければならない ユーザー提供関数関数は、それがユーザー宣言されており、かつ最初の宣言で明示的にデフォルト化または削除されていない場合、 user-provided となります。ユーザー提供の明示的デフォルト化関数(すなわち、最初の宣言後に明示的にデフォルト化されたもの)は、明示的にデフォルト化された時点で定義されます。このような関数が暗黙的に削除済みとして定義される場合、プログラムは不適格となります。最初の宣言後に関数をデフォルト化として宣言することは、効率的な実行と簡潔な定義を提供するとともに、進化するコードベースへの安定したバイナリインターフェースを可能にします。 // 「trivial」のすべての特殊メンバ関数は // それぞれ最初の宣言でデフォルト化されており、 // ユーザー提供ではない struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator=(const trivial&) = default; trivial& operator=(trivial&&) = default; ~trivial() = default; }; struct nontrivial { nontrivial(); // 最初の宣言 }; // 最初の宣言でデフォルト化されておらず、 // ユーザー提供であり、ここで定義されている nontrivial::nontrivial() = default; 曖昧性解決
関数本体と
初期化子
の間の曖昧性が生じる場合で、かつ
using T = void(); // 関数型 using U = int; // 非関数型 T a{}; // 何もしない関数を定義 U b{}; // intオブジェクトを値初期化 T c = delete("hello"); // 関数をdeleteとして定義 U d = delete("hello"); // intオブジェクトをdelete式の結果で // コピー初期化(不適格)
__func__関数本体内部では、関数ローカルで事前定義された変数 __func__ は、以下のように定義されているかのように扱われます static const char __func__[] = "function-name"; この変数はブロックスコープと静的記憶域期間を持ちます: struct S { S(): s(__func__) {} // OK: 初期化リストは関数本体の一部 const char* s; }; void f(const char* s = __func__); // エラー: パラメータリストは宣言子の一部 |
(C++11以降) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
関数契約指定子関数宣言と lambda expressions は一連の function contract specifiers を含むことができ、各指定子は以下の構文を持ちます:
1)
事前条件アサーションを導入します。
2,3)
ポストコンディション表明を導入する。
2)
アサーションは結果にバインドされません。
3)
アサーションは結果にバインドされます。
関数契約表明は、関数に関連付けられた 契約表明 です。関数契約表明の述語は、 述語 が 文脈的に変換 され bool 型となったものです。 以下の関数は関数契約指定子と共に宣言することはできません:
事前条件アサーション事前条件アサーションは関数のエントリに関連付けられています: int divide(int dividend, int divisor) pre(divisor != 0) { return dividend / divisor; } double square_root(double num) pre(num >= 0) { return std::sqrt(num); } 事後条件アサーション事後条件アサーションは、関数が正常に終了する際に関連付けられます。 事後条件アサーションに identifier が含まれている場合、関数契約指定子は identifier を関連する関数の 結果バインディング の名前として導入します。結果バインディングは、その関数の呼び出しによって返されるオブジェクトまたは参照を表します。結果バインディングの型は、関連する関数の戻り値の型です。 int absolute_value(int num) post(r : r >= 0) { return std::abs(num); } double sine(double num) post(r : r >= -1.0 && r <= 1.0) { if (std::isnan(num) || std::isinf(num)) // 例外による終了は契約違反を引き起こさない throw std::invalid_argument("無効な引数"); return std::sin(num); } 事後条件表明が identifier を持つ場合、かつ関連する関数の戻り値の型が(possibly cv-qualified) void である場合、プログラムは不適格となります: void f() post(r : r > 0); // エラー: 「r」に値をバインドできません 非テンプレート関数の宣言された戻り値の型に プレースホルダ型 が含まれる場合、 identifier を含む事後条件アサーションは関数定義内でのみ出現できます: auto g(auto&) post(r : r >= 0); // OK、「g」はテンプレート auto h() post(r : r >= 0); // エラー: 戻り値に名前を付けることはできません auto k() post(r : r >= 0) // OK、「k」は定義 { return 0; } 契約の一貫性
関数または関数テンプレート
func
の
再宣言
宣言
2つの contract-specs は、同じ順序で同じ関数契約指定子で構成されている場合、同一と見なされます。
関数宣言
この条件が、単に述語内に含まれる2つのラムダ式の比較のみが原因で満たされない場合、 診断は要求されません。 bool b1, b2; void f() pre (b1) pre([]{ return b2; }()); void f(); // OK、関数契約指定子が省略されています void f() pre (b1) pre([]{ return b2; }()); // エラー:クロージャの型が異なります void f() pre (b1); // エラー:関数契約指定子が異なります int g() post(r : b1); int g() post(b1); // エラー:結果のバインドがありません namespace N { void h() pre (b1); bool b1; void h() pre (b1); // エラー:関数契約指定子が // 一定義規則に従って異なります } |
(C++26以降) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
注記
直接初期化構文を使用した変数宣言と関数宣言の間で曖昧性がある場合、コンパイラは常に関数宣言を選択します。詳細は direct-initialization を参照してください。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_decltype_auto
|
201304L
|
(C++14) |
decltype(auto)
|
__cpp_return_type_deduction
|
201304L
|
(C++14) | 通常関数の戻り値型推論 |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) | 明示的オブジェクトパラメータ ( this の推論 ) |
__cpp_deleted_function
|
202403L
|
(C++26) | 理由付き削除関数 |
キーワード
例
#include <iostream> #include <string> // デフォルト引数を持つシンプルな関数、戻り値なし void f0(const std::string& arg = "world!") { std::cout << "Hello, " << arg << '\n'; } // 宣言は名前空間(ファイル)スコープ内 //(定義は後で提供) int f1(); // f0へのポインタを返す関数、C++11以前のスタイル void (*fp03())(const std::string&) { return f0; } // f0へのポインタを返す関数、C++11の後置戻り値型 auto fp11() -> void(*)(const std::string&) { return f0; } int main() { f0(); fp03()("test!"); fp11()("again!"); int f2(std::string) noexcept; // 関数スコープ内の宣言 std::cout << "f2(\"bad\"): " << f2("bad") << '\n'; std::cout << "f2(\"42\"): " << f2("42") << '\n'; } // intを返すシンプルな非メンバ関数 int f1() { return 007; } // 例外指定と関数tryブロックを持つ関数 int f2(std::string str) noexcept try { return std::stoi(str); } catch (const std::exception& e) { std::cerr << "stoi() failed!\n"; return 0; } // 削除された関数、呼び出しを試みるとコンパイルエラーになる void bar() = delete # if __cpp_deleted_function ("reason") # endif ;
出力例:
stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 135 | C++98 |
クラス内で定義されたメンバー関数は
自身のクラスをパラメータまたは戻り値の型として 持つことができなかった(不完全型のため) |
許可 |
| CWG 332 | C++98 | パラメータがCV修飾された void 型を持つことができた | 禁止 |
| CWG 393 | C++98 |
未知のサイズの配列へのポインタ/参照を含む型は
パラメータとして使用できなかった |
そのような型は許可 |
| CWG 452 | C++98 | メンバー初期化子リストは関数本体の一部ではなかった | 一部である |
| CWG 577 | C++98 |
依存型
void
を使用して
パラメータなしの関数を宣言できた |
非依存
void のみ許可 |
| CWG 1327 | C++11 |
デフォルト化または削除された関数は
override または final で指定できなかった |
許可 |
| CWG 1355 | C++11 | 特殊メンバー関数のみがユーザー提供可能だった | すべての関数に拡張 |
| CWG 1394 | C++11 |
削除された関数は不完全型のパラメータを
持つことや不完全型を返すことができなかった |
不完全型が許可 |
| CWG 1824 | C++98 |
関数定義のパラメータ型と戻り値型に対する完全性チェックが
関数定義のコンテキスト外で行われる可能性があった |
関数定義の
コンテキスト内でのみ チェック |
| CWG 1877 | C++14 | 戻り値型推論が return ; を return void ( ) ; として扱った |
この場合、単純に戻り値
型を void と推論 |
| CWG 2015 | C++11 | 削除された仮想関数の暗黙的なodr-useは不適格だった |
そのようなodr-useは
使用禁止から免除される |
| CWG 2044 | C++14 |
void
を返す関数に対する戻り値型推論は
宣言された戻り値型が decltype ( auto ) の場合に失敗した |
このケースを処理するよう
推論規則を更新 |
| CWG 2081 | C++14 |
関数の再宣言は、最初の宣言で戻り値型推論を
使用していなくても使用できた |
許可されない |
| CWG 2144 | C++11 | { } が同じ場所で関数本体または初期化子の両方になり得た |
宣言子識別子の型によって
区別される |
| CWG 2145 | C++98 | 関数定義内の 宣言子 を括弧で囲むことができなかった | 許可 |
| CWG 2259 | C++11 |
括弧で囲まれた型名に関する曖昧性解決規則が
ラムダ式をカバーしていなかった |
カバーされる |
| CWG 2430 | C++98 |
クラス定義内のメンバー関数定義において、
そのクラスの型が戻り値型またはパラメータ型に なれなかった( CWG issue 1824 の解決による) |
関数本体内でのみ
チェック |
| CWG 2760 | C++98 |
コンストラクタの関数本体は、コンストラクタの通常の
関数本体で指定されていない初期化を含まなかった |
これらの初期化も
含む |
| CWG 2831 | C++20 |
requires-clause
を持つ関数定義が
非テンプレート関数を定義できた |
禁止 |
| CWG 2846 | C++23 | 明示的オブジェクトメンバー関数はクラス外定義を持てなかった | 許可 |
| CWG 2915 | C++23 | 無名の明示的オブジェクトパラメータが型 void を持つことができた | 禁止 |
関連項目
|
C ドキュメント
for
関数宣言
|