定義
C++では,宣言や定義を行うことで関数を利用できる.関数とは,複合文のことである.関数はユーザが定義したいくつかの引数と呼ばれる項を受け取り,特定のオブジェクトを1つ返す.この時返却値の型にvoidを設定することで何も返さないことができる.また,返却値のある関数を呼び出す場合であっても,呼び出し側が受け取らないことは可能であるが,返却値の値によってエラーやその種類を表すものもあるので,プログラマはその関数の返却値が何なのか意識しながらプログラミングを行うべきである.
プログラマは定義された関数に対し次の2つの処理を行える.
- 関数呼び出し
関数呼び出し()演算子を使用することで,関数を呼び出すことができる.なお,引数は演算子の時と同様計算順序は未定である.
- 関数の先頭アドレスを取得
関数の名前を式とした時,演算結果は関数の先頭アドレスになる.
関数のポインタ
int func1(int i){return i+1;}
int func2()
{
int (*hoge)(int);
return hoge(2);
}
- function-definition
- decl-specifier-listopt declarator ctor-initializeropt function-body
- decl-specifier-listopt declarator function-try-body
- decl-specifier-list
- decl-specifier-listopt decl-specifier
- decl-specifier
- storage-class-specifier
- type-specifier
- function-specifier
- friend
- typedef
- id-expression
- unqualified-id
- qualified-id
- qualified-id
- identifier
- operator-function-id
- conversion-function-id
- ~class-name
- template-id
- unqualified-id
- function-specifier
- inline
- virtual
- explicit
- declarator
- declarator-id
- ptr-operator declarator
- declarator ( parameter-declaration-clause ) cv-qualifier-listopt exception-specificationopt
- declarator [ constant-expressionopt ]
- (declarator )
- declarator-id
- ::opt id-expression
- ::opt nested-name-specifieropt type-name
- ptr-operator
- * cv-qualifier-listopt
- & cv-qualifier-listopt
- nested-name-specifier :: * cv-qualifier-listopt
- ctor-initializer
- : mem-initializer-list
- mem-initializer-list
- mem-initializer
- mem-initializer , mem-initializer-list
- mem-initializer
- mem-initializer-id (expression-listopt )
- mem-initializer-id
- ::opt nested-name-specifieropt class-name
- identifier
- function-body
- compound-statement
- function-try-body
- try ctor-initializeropt function-body handler-list
- handler-list
- catch ( exception-declaration ) compound-statement
- operator-function-id
- operator operator
- parameter-declaration-clause
- parameter-declaration-listopt ...opt
- parameter-declaration-list , ...
- parameter-declaration-list
- parameter-declaration
- parameter-declaration-list , parameter-declaration
- parameter-declaration
- decl-specifier-list declarator
- decl-specifier-list declarator = assignment-expression
- decl-specifier-list abstract-declarator
- decl-specifier-list abstract-declarator = assignment-expression
- assignment-expression
- conditional-expression
- logical-or-expression assignment-operator assignment-expression
- throw-expression
- その他
- cv-qualifier-list はconstかvolatileの修飾子による0回以上の修飾のこと.
- exception-specificationは
多重定義(オーバーロード)
関数の引数の型やその数,返り値の型はその関数に依存する.しかし,同じスコープで,コンセプトが同じ関数に同じ名前を付けることができれば,プログラマは柔軟な記述ができる.
例えば2つの引き数を受け取って合計値を返すadd関数を定義する時(加算演算子を使えというツッコミはなしで),double型の引数を2つ受け取り,それらを加算して結果のdouble型を返す関数と,int型の引数を2つ受け取り,それらを加算して結果のint型を返す関数を同名にできれば,便利である.C++では引数や返り値の型だけでなく,引数の数もいろいろ設定できる.
ただし,次のような場合は多重定義できない.
- 引数の型がTかT&の差しかない場合(これはT&がTの別名なだけで同じ型であるためである).
- typedefされた型とその元々の型しか差がない場合.
- Tがconst Tやvolatile Tになっただけの場合(ただし次の2つは区別される).
- const T&とvolatile T&,指定子なしのT&.
- const T*とvolatile T*,指定子なしのT*.
- 違いがポインタか配列だけの場合.
- 2次元配列以降が同じ場合(多次元配列の場合,2次元配列以降に意味がある).
- g(char[5][10])
- g(char[][10])
- g(char[7][10])
- g(char[5][20])
- g(char[5][3][10])
- g(char[3][3][10])
- g(char[5][10][10])
- aを定義した時,bとcは定義できない
- eを定義した時,fは定義できない
- charとunsigned char,signed charはそれぞれ別の型
- 引数が同じで返り値の型だけが異なる場合.
例えば,
多重定義関数の曖昧な呼び出し.
多重定義可能であっても,それを呼び出す際にどの関数を呼び出しているか特定できなければ言語処理系はエラーとする. これは,関数呼び出しの引数が受け取り側と常に完全一致する場合は,問題ない. しかし暗黙的な型変換を行なった場合に限り引数が一致するような時,言語処理系は次のアルゴリズムに基づいて適切な関数を選出する.
- 正確な一致か単純変換での一致(ただし前述で示したものは多重定義できず,↓のような一部の単純変換はそれ以外の単純変換より優劣が低い).
- T& → const T&
- T& → volatile T&
- T* → const T*
- T* → volatile T*
- 格上げ(整数格上げや浮動小数点格上げ)による一致.
- 標準変換による一致.
- ユーザ定義変換による一致.
- 関数宣言に省略符号(...)を使ったら一致.
クラスBがクラスAから直接的または間接的な公開派生したクラスである場合,B*からA*への変換は,B*からvoid*やconst void*への変換よりも優先度が高い(派生についての詳細は「オブジェクト指向プログラミング」を参照されたし).そしてクラスCがクラスBから直接的または間接的な公開派生したクラスである場合,C*からB*への変換は,C*からA*への変換よりも優れている.C&からB&への変換は,C&からA&への変換も同様である.
また次の場合もまた多重定義可能であっても利用時に曖昧であるためエラーになる.
- 非constな型の引数にリテラル.
- デフォルト引数を考慮した時,どちらか分からない場合
- 非constな型の引数にリテラル(一時オブジェクトが必要な場合).
void func(int *i){return;} void func(const int *i){return;} int main() { func(NULL); return 0; }
- デフォルト引数を考慮した時,どちらか分からない場合
void func(){return;} void func(int x = 0){return;} int main() { func(); return 0; }