関数宣言
関数宣言は関数名とその型を導入します。関数定義は関数名/型と関数本体を関連付けます。
目次 |
[編集] 関数宣言
関数宣言は任意のスコープに現れることができます。クラススコープでの関数宣言は、クラスのメンバー関数を導入します (friend 指定子が使用されている場合を除きます)。詳細はメンバー関数とフレンド関数を参照してください。
noptr-declarator ( parameter-list ) cv (オプション) ref (オプション) except (オプション) attr (オプション) |
(1) | ||||||||
noptr-declarator ( parameter-list ) cv (オプション) ref (オプション) except (オプション) attr (オプション)-> trailing |
(2) | (C++11以降) | |||||||
(declarator 構文の他の形式については宣言を参照)
| noptr-declarator | - | 任意の有効な宣言子ですが、*、&、または&&で始まる場合は括弧で囲む必要があります。 | ||||||
| parameter-list | - | 関数のパラメータの、コンマで区切られたリスト (空の場合もあります) (詳細は以下を参照) | ||||||
| attr | - | (C++11以降) 属性のリスト。これらの属性は、関数自体ではなく、関数の型に適用されます。関数の属性は宣言子内の識別子の後に現れ、宣言の先頭に現れる属性があれば、それらと結合されます。 | ||||||
| cv | - | const/volatile修飾、非静的メンバー関数宣言でのみ許可 | ||||||
| ref | - | (C++11以降) ref-修飾、非静的メンバー関数宣言でのみ許可 | ||||||
| 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以降) |
関数宣言子は、宣言指定子シーケンスが許す限り、他の宣言子と混在させることができます。
// declares an int, an int*, a function, and a pointer to a function int a = 1, *p = NULL, f(), (*pf)(double); // decl-specifier-seq is int // declarator f() declares (but doesn't define) // a function taking no arguments and returning int struct S { virtual int f(char) const, g(int) &&; // declares two non-static member functions virtual int f(char), x; // compile-time error: virtual (in decl-specifier-seq) // is only allowed in declarations of non-static // member functions };
|
volatile修飾されたオブジェクト型をパラメータ型または戻り値の型として使用することは非推奨です。 |
(C++20以降) |
関数の戻り値の型は関数型や配列型にすることはできません(ただし、それらへのポインタや参照は可能です)。
|
他の宣言と同様に、宣言の前に現れる属性と、宣言子内の識別子の直後に現れる属性は、宣言または定義されているエンティティ(この場合は関数)に適用されます。 [[noreturn]] void f [[noreturn]] (); // OK: both attributes apply to the function f ただし、宣言子の後に現れる属性(上記の構文)は、関数自体ではなく、関数の型に適用されます。 void f() [[noreturn]]; // Error: this attribute has no effect on the function itself |
(C++11以降) |
戻り値の型推論関数宣言のdecl-specifier-seqにキーワードautoが含まれている場合、後続の戻り値の型は省略でき、return文で使用される式の型からコンパイラによって推論されます。戻り値の型がdecltype(auto)を使用しない場合、推論はテンプレート引数推論の規則に従います。 int x = 1; auto f() { return x; } // return type is int const auto& f() { return x; } // return type is const int& 戻り値の型がdecltype(auto)の場合、戻り値の型は、return文で使用される式が int x = 1; decltype(auto) f() { return x; } // return type is int, same as decltype(x) decltype(auto) f() { return(x); } // return type is int&, same as decltype((x)) (注: 「const decltype(auto)&」はエラーです。decltype(auto)は単独で使用する必要があります) 複数のreturn文がある場合、それらはすべて同じ型に推論されなければなりません。 auto f(bool val) { if (val) return 123; // deduces return type int else return 3.14f; // Error: deduces return type float } return文がない場合、またはreturn文の引数がvoid式である場合、宣言された戻り値の型はdecltype(auto)である必要があります。この場合、推論された戻り値の型はvoidになります。あるいは(cv修飾されていてもよい)autoである必要があります。この場合、推論された戻り値の型は(同様にcv修飾された)voidになります。 auto f() {} // returns void auto g() { return f(); } // returns void auto* x() {} // Error: cannot deduce auto* from void 関数内で一度return文が確認されると、その文から推論された戻り値の型は、他のreturn文を含む関数の残りの部分で使用できます。 auto sum(int i) { if (i == 1) return i; // sum’s return type is int else return sum(i - 1) + i; // OK: sum’s return type is already known } return文がブレースで囲まれた初期化子リストを使用する場合、推論は許可されません。 auto func() { return {1, 2, 3}; } // Error 仮想関数とコルーチン(C++20以降)は戻り値の型推論を使用できません。 struct F { virtual auto f() { return 2; } // Error }; ユーザー定義変換関数以外の関数テンプレートは戻り値の型推論を使用できます。return文の式が依存しなくても、推論はインスタンス化時に行われます。このインスタンス化は、SFINAEの目的のための即時コンテキストではありません。 template<class T> auto f(T t) { return t; } typedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type template<class T> auto f(T* t) { return *t; } void g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types, // chooses second template overload 戻り値の型推論を使用する関数または関数テンプレートの再宣言または特殊化は、同じ戻り値の型プレースホルダーを使用する必要があります。 auto f(int num) { return num; } // int f(int num); // Error: no placeholder return type // decltype(auto) f(int num); // Error: different placeholder template<typename T> auto g(T t) { return t; } template auto g(int); // OK: return type is int // template char g(char); // Error: not a specialization of the primary template g 同様に、戻り値の型推論を使用しない関数または関数テンプレートの再宣言または特殊化は、プレースホルダーを使用してはなりません。 int f(int num); // auto f(int num) { return num; } // Error: not a redeclaration of f template<typename T> T g(T t) { return t; } template int g(int); // OK: specialize T as int // template auto g(char); // Error: not a specialization of the primary template g 明示的インスタンス化宣言は、戻り値の型推論を使用する関数テンプレート自体をインスタンス化しません。 template<typename T> auto f(T t) { return t; } extern template auto f(int); // does not instantiate f<int> int (*p)(int) = f; // instantiates f<int> to determine its return type, // but an explicit instantiation definition // is still required somewhere in the program |
(C++14以降) |
[編集] パラメータリスト
パラメータリストは、関数が呼び出されたときに指定できる引数を決定します。これは、コンマで区切られたパラメータ宣言のリストであり、それぞれが以下の構文を持ちます。
| 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は型指定子以外の指定子が存在し得ることを示唆していますが、許可されている他の指定子はregisterおよびauto(C++11まで)のみであり、効果はありません。 |
(C++17まで) |
|
いずれかの関数パラメータがプレースホルダー(autoまたはコンセプト型)を使用する場合、その関数宣言は省略関数テンプレート宣言となります。 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))を持つパラメータ宣言は、明示的オブジェクトパラメータを宣言します。 明示的オブジェクトパラメータは関数パラメータパックにすることはできず、以下の宣言のパラメータリストの最初のパラメータとしてのみ現れることができます。
明示的なオブジェクトパラメータを持つメンバー関数には、以下の制限があります。 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から) |
関数宣言で宣言されたパラメータ名は、通常、自己文書化の目的でしかありません。それらは関数定義で使用されます(ただし、オプションのままです)。
パラメータリスト内で型名が括弧で囲まれている場合(ラムダ式を含む(C++11以降))に曖昧さが生じます。この場合、関数へのポインタ型のパラメータの宣言と、declaratorの識別子を余分な括弧で囲んだパラメータの宣言との間で選択が行われます。解決策は、型名を単純型指定子(関数ポインタ型)とみなすことです。
class C {}; void f(int(C)) {} // void f(int(*fp)(C param)) {} // NOT void f(int C) {} void g(int *(C[10])); // void g(int *(*fp)(C param[10])); // NOT void g(int *C[10]);
パラメータ型は、参照や不明な境界の配列へのポインタ、そのような型の多重ポインタ/配列、またはパラメータがそのような型である関数へのポインタを含む型にすることはできません。
[編集] 省略記号の使用
パラメータリストの最後のパラメータは省略記号(...)にすることができます。これは可変引数関数を宣言します。省略記号の前のコンマは省略できます(C++26で非推奨)
int printf(const char* fmt, ...); // a variadic function int printf(const char* fmt...); // same as above, but deprecated since C++26 template<typename... Args> void f(Args..., ...); // a variadic function template with a parameter pack template<typename... Args> void f(Args... ...); // same as above, but deprecated since C++26 template<typename... Args> void f(Args......); // same as above, but deprecated since C++26
[編集] 関数型
[編集] パラメータ型リスト
関数のパラメータ型リストは次のように決定されます。
- 各パラメータの型(関数パラメータパックを含む)(C++11以降)は、それぞれのパラメータ宣言から決定されます。
- 各パラメータの型を決定した後、「
Tの配列」型または関数型Tのパラメータは、「Tへのポインタ」に調整されます。 - パラメータ型リストを作成した後、関数型を形成する際に、パラメータ型を変更するトップレベルのcv修飾子は削除されます。
- 結果として得られる変換されたパラメータ型のリストと、省略記号または関数パラメータパック(C++11以降)の有無が、関数のパラメータ型リストとなります。
void f(char*); // #1 void f(char[]) {} // defines #1 void f(const char*) {} // OK, another overload void f(char* const) {} // Error: redefines #1 void g(char(*)[2]); // #2 void g(char[3][2]) {} // defines #2 void g(char[3][3]) {} // OK, another overload void h(int x(const int)); // #3 void h(int (*)(int)) {} // defines #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以降) |
// the type of “f1” is // “function of int returning void, with attribute noreturn” void f1(int a) [[noreturn]]; // the type of “f2” is // “constexpr noexcept function of pointer to int returning int” constexpr auto f2(int[] b) noexcept -> int; struct X { // the type of “f3” is // “function of no parameter const returning const int” const int f3() const; };
[編集] 後続の修飾子
cv またはref (C++11以降)を持つ関数型(typedef名で命名された型を含む)は、次の場合にのみ現れます。
- 非静的メンバー関数の関数型
- メンバーへのポインタが指す関数型
- 関数typedef宣言またはエイリアス宣言(C++11以降)のトップレベル関数型
- テンプレート型パラメータのデフォルト引数内の型ID、または
- テンプレート型パラメータのテンプレート引数の型ID。
typedef int FIC(int) const; FIC f; // Error: does not declare a member function struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK
[編集] 関数シグネチャ
すべての関数にはシグネチャがあります。
関数のシグネチャは、その名前とパラメータ型リストで構成されます。そのシグネチャには、囲んでいる名前空間も含まれますが、以下の例外があります。
- 関数がメンバー関数である場合、そのシグネチャには、囲む名前空間の代わりに、関数がメンバーであるクラスが含まれます。そのシグネチャには、存在する場合、以下のコンポーネントも含まれます。
- cv
|
(C++11以降) |
|
(C++20以降) |
exceptとattr(C++11以降)は関数シグネチャには関与しませんが、noexcept指定は関数型に影響を与えます(C++17以降)。
[編集] 関数定義
非メンバー関数定義は名前空間スコープでのみ現れます(ネストされた関数はありません)。メンバー関数定義は、クラス定義の本体内にも現れることがあります。これらは以下の構文を持ちます。
| 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は以下のいずれかです。
| ctor-initializer (オプション) compound-statement | (1) | ||||||||
| function-try-block | (2) | ||||||||
= default ; |
(3) | (C++11以降) | |||||||
= delete ; |
(4) | (C++11以降) | |||||||
= delete ( string-literal ); |
(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 is “int” // declarator is “max(int a, int b, int c)” // body is { ... }
関数本体は複合文(一対の波括弧で囲まれた0個以上の文のシーケンス)であり、関数が呼び出されたときに実行されます。さらに、コンストラクタの関数本体には以下のものも含まれます。
- コンストラクタのメンバー初期化子リストに識別子がないすべての非静的データメンバーに対して、対応するメンバーサブオブジェクトを初期化するために使用されるデフォルトメンバー初期化子または(C++11以降)デフォルト初期化。
- コンストラクタのメンバー初期化子リストに型名がないすべての基底クラスに対して、対応する基底クラスのサブオブジェクトを初期化するために使用されるデフォルト初期化。
|
関数定義にvirt-specsが含まれる場合、それはメンバー関数を定義しなければなりません。 |
(C++11以降) |
|
関数定義にrequires-clauseが含まれる場合、それはテンプレート関数を定義しなければなりません。 |
(C++20以降) |
void f() override {} // Error: not a member function void g() requires (sizeof(int) == 4) {} // Error: not a templated function
関数定義のパラメータ型、および戻り値の型は、(cv修飾されていてもよい)不完全なクラス型にすることはできません(関数が削除として定義されている場合を除く)(C++11以降)。完全性のチェックは関数本体内でのみ行われるため、メンバー関数は、定義時点では不完全であっても(関数本体内では完全です)、定義されているクラス(またはその囲むクラス)を返すことができます。
関数定義のdeclaratorで宣言されたパラメータは、本体内ではスコープ内にあります。パラメータが関数本体で使用されない場合、名前を付ける必要はありません(抽象宣言子を使用するだけで十分です)。
void print(int a, int) // second parameter is not used { std::printf("a = %d\n", a); }
パラメータのトップレベルのcv修飾子は関数宣言では破棄されますが、関数本体で可視となるパラメータの型を変更します。
void f(const int n) // declares function of type void(int) { // but in the body, the type of “n” is const int }
デフォルト化された関数関数定義が構文(3)である場合、その関数は明示的にデフォルト化されたものとして定義されます。 明示的にデフォルト化された関数は特殊メンバー関数または比較演算子関数(C++20以降)でなければならず、デフォルト引数を持ってはなりません。 明示的にデフォルト化された特殊メンバー関数
最初の宣言で明示的にデフォルト化された関数は暗黙的にインラインであり、constexpr関数になり得る場合は暗黙的にconstexprです。 struct S { S(int a = 0) = default; // error: default argument void operator=(const S&) = default; // error: non-matching return type ~S() noexcept(false) = default; // OK, different exception specification private: int i; S(S&); // OK, private copy constructor }; S::S(S&) = default; // OK, defines copy constructor 明示的にデフォルト化された関数と暗黙的に宣言された関数は、まとめてデフォルト化された関数と呼ばれます。それらの実際の定義は暗黙的に提供されます。詳細はそれぞれのページを参照してください。 削除された関数関数定義が構文(4)または(5)(C++26以降)である場合、その関数は明示的に削除されたものとして定義されます。 削除された関数の使用は不正です(プログラムはコンパイルされません)。これには、明示的な呼び出し(関数呼び出し演算子を使用)と暗黙的な呼び出し(削除されたオーバーロード演算子、特殊メンバー関数、アロケーション関数などへの呼び出し)、削除された関数へのポインタまたはメンバーへのポインタの構築、さらには潜在的に評価されることのない式での削除された関数の使用も含まれます。 純粋仮想ではないメンバー関数は、暗黙的にODR-usedされるにもかかわらず、削除済みとして定義できます。削除済み関数は削除済み関数によってのみオーバーライドでき、非削除済み関数は非削除済み関数によってのみオーバーライドできます。
関数がオーバーロードされている場合、まずオーバーロード解決が行われ、削除された関数が選択された場合にのみプログラムが不正となります。 struct T { void* operator new(std::size_t) = delete; void* operator new[](std::size_t) = delete("new[] is deleted"); // since C++26 }; T* p = new T; // Error: attempts to call deleted T::operator new T* p = new T[5]; // Error: attempts to call deleted T::operator new[], // emits a diagnostic message “new[] is deleted” 関数の削除された定義は、翻訳単位における最初の宣言でなければなりません。以前に宣言された関数を削除されたものとして再宣言することはできません。 struct T { T(); }; T::T() = delete; // Error: must be deleted on the first declaration ユーザー定義関数関数がユーザー定義であるとは、それがユーザーによって宣言され、最初の宣言で明示的にデフォルト化または削除されていない場合を指します。ユーザー定義の明示的にデフォルト化された関数(つまり、最初の宣言後に明示的にデフォルト化された関数)は、明示的にデフォルト化された時点で定義されます。そのような関数が暗黙的に削除されたものとして定義された場合、プログラムは不正です。最初の宣言後に関数をデフォルト化されたものとして宣言することで、進化するコードベースに対して安定したバイナリインターフェースを可能にしながら、効率的な実行と簡潔な定義を提供できます。 // All special member functions of “trivial” are // defaulted on their first declarations respectively, // they are not user-provided 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(); // first declaration }; // not defaulted on the first declaration, // it is user-provided and is defined here nontrivial::nontrivial() = default; 曖昧性解決関数本体と
using T = void(); // function type using U = int; // non-function type T a{}; // defines a function doing nothing U b{}; // value-initializes an int object T c = delete("hello"); // defines a function as deleted U d = delete("hello"); // copy-initializes an int object with // the result of a delete expression (ill-formed)
__func__関数本体内では、関数ローカルの事前定義変数__func__は、次のように定義されます。 static const char __func__[] = "function-name"; この変数はブロックスコープと静的記憶域期間を持ちます。 struct S { S(): s(__func__) {} // OK: initializer-list is part of function body const char* s; }; void f(const char* s = __func__); // Error: parameter-list is part of declarator |
(C++11以降) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
関数契約指定子関数宣言とラムダ式は、関数契約指定子のシーケンスを含むことができます。各指定子は以下の構文を持ちます。
1) 事前条件アサーションを導入します。
2,3) 事後条件アサーションを導入します。
2) アサーションは結果に束縛されません。
3) アサーションは結果に束縛されます。
関数契約アサーションは、関数に関連付けられた契約アサーションです。関数契約アサーションの述語は、そのpredicateを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)) // exiting via an exception never causes contract violation throw std::invalid_argument("Invalid argument"); return std::sin(num); } 事後条件アサーションにidentifier があり、関連する関数の戻り値の型が(cv修飾されている可能性のある)voidである場合、プログラムは不正となります。 void f() post(r : r > 0); // Error: no value can be bound to “r” 非テンプレート関数の宣言された戻り値の型にプレースホルダー型が含まれる場合、identifier を持つ事後条件アサーションは、関数定義内にのみ現れることができます。 auto g(auto&) post(r : r >= 0); // OK, “g” is a template auto h() post(r : r >= 0); // Error: cannot name the return value auto k() post(r : r >= 0) // OK, “k” is a definition { return 0; } 契約の一貫性関数または関数テンプレートfuncの再宣言 ある翻訳単位における関数funcの最初の宣言が 2つのcontract-specs は、同じ関数契約指定子を同じ順序で含む場合、同じであるとみなされます。 関数宣言
この条件がpredicate 内に含まれる2つのラムダ式の比較のみによって満たされない場合、診断は必要ありません。 bool b1, b2; void f() pre (b1) pre([]{ return b2; }()); void f(); // OK, function contract specifiers omitted void f() pre (b1) pre([]{ return b2; }()); // Error: closures have different types void f() pre (b1); // Error: function contract specifiers are different int g() post(r : b1); int g() post(b1); // Error: no result binding namespace N { void h() pre (b1); bool b1; void h() pre (b1); // Error: function contract specifiers differ // according to the one−definition rule } |
(C++26以降) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[編集] 備考
直接初期化構文を用いた変数宣言と関数宣言の間に曖昧さがある場合、コンパイラは常に関数宣言を選択します。直接初期化を参照してください。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__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> // simple function with a default argument, returning nothing void f0(const std::string& arg = "world!") { std::cout << "Hello, " << arg << '\n'; } // the declaration is in namespace (file) scope // (the definition is provided later) int f1(); // function returning a pointer to f0, pre-C++11 style void (*fp03())(const std::string&) { return f0; } // function returning a pointer to f0, with C++11 trailing return type auto fp11() -> void(*)(const std::string&) { return f0; } int main() { f0(); fp03()("test!"); fp11()("again!"); int f2(std::string) noexcept; // declaration in function scope std::cout << "f2(\"bad\"): " << f2("bad") << '\n'; std::cout << "f2(\"42\"): " << f2("42") << '\n'; } // simple non-member function returning int int f1() { return 007; } // function with an exception specification and a function try block int f2(std::string str) noexcept try { return std::stoi(str); } catch (const std::exception& e) { std::cerr << "stoi() failed!\n"; return 0; } // deleted function, an attempt to call it results in a compilation error 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 | 関数定義のdeclaratorは括弧で囲むことができませんでした。 | 許可 |
| 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ドキュメント関数宣言
|