組み込みC/C++

C/C++リテラシー向上のためのページ

関数プロトタイプの混乱③

C++で追加されたデフォルト引数について見ていきましょう。

 

Pattern 6

double func(int i_a, int i_b );

int main(){

    func(3);
}

double func(int i_a, int i_b ){

    printf("i_a=%d\n",i_a);

	return(0);
}

 

Pattern 6はCではコンパイルできますが、C++ではエラーとなります。Cの場合は引数の曖昧さのためにコンパイルが通りますが、C++では関数のオーバーロードがある為、プロトタイプ宣言、関数呼び出し、関数の実体、この三つの引数の型と数とを厳密に合わせないと違う関数と見なされます。(なお戻り値に関してはラベル名に反映されない為、戻り値が違う同名関数を定義するとその時点でコンパイルエラーです。)

 

Pattern 7

double func(int i_a, int i_b = 0 );

int main(){

	func(3);
}

double func(int i_a, int i_b ){

	printf("i_a=%d\n",i_a);

	return(0);
}

 Pattern 7はどうでしょうか?これはCではデフォルト引数という仕様がないのでエラーになりますが、C++ではコンパイルできます。先ほど厳密に引数の数を合わせろといったばかりですが、デフォルト引数がある場合は呼び出し時の引数の数があっていなくても同じ関数とみなせるのです。この事は関数のオーバーロードの曖昧さを生み出します。Pattern 7にオーバーロードする関数を追加してみるとどうなるでしょうか?

 

Pattern 8

int func(int i_a );
double func(int i_a, int i_b = 0 );

int main(){

    func(3);
}

int func(int i_a){

	printf("int func(int i_a)\n");

	return(0);

}

double func(int i_a, int i_b ){

	printf("double func(int i_a, int i_b)\n");

	return(0);
}

 

これは関数の呼び出し部分で「error C2668: 'func' : オーバーロード関数の呼び出しを解決することができません。(新機能 ; ヘルプを参照)」というエラーとなります。さて、デフォルト引数は関数のプロトタイプ宣言、もしくは関数の定義上でしていいのでした。デフォルト引数の定義場所を変えてみましょう。

 

Pattern 9

int func(int i_a );
double func(int i_a, int i_b );

int main(){

    func(3);
}

int func(int i_a){

    printf("int func(int i_a)\n");

	return(0);

}

double func(int i_a, int i_b = 0 ){

	printf("double func(int i_a, int i_b)\n");

	return(0);
}

 Pattern 9はC++でもコンパイル出来てしまいます。そして実際に呼び出されるのは引数が一つのfunc(int i_a); の方です。引数が二つの方の関数は引数一つで呼び出される事はありません。容易に混乱を招く事が想像つきます。さてではPattern 7に戻って、デフォルト引数を実体側に移すとどうなるでしょうか?

 

Pattern 10

double func(int i_a, int i_b );

int main(){

    func(3);
}

double func(int i_a, int i_b = 0 ){

	printf("i_a=%d\n",i_a);

	return(0);
}

 残念ながらこれは「error C2660: 'func' : 関数に 1 個の引数を指定できません。」というコンパイルエラーです。というわけで実際に実体側にデフォルト引数をつけられるパターンというのは、前方宣言なし、実体を呼び出しより前に定義している場合に限られます。デフォルト引数は基本的にプロトタイプ宣言のみで行い、コンストラクタのような宣言時に実体を定義する場合はつけると考えておくと良いかと思います。

 

まとめ

①デフォルト引数はプロトタイプ宣言のみで行う事。

②デフォルト引数を使うと呼び出し時の曖昧さが生じる為、関数のオーバーロードで意図しない関数が呼ばれる事がある。

 

今回はデフォルト引数があった場合の関数のオーバーロードの注意点を見てみました。また今回取り扱いませんでしたが、これに外部結合が絡んでくると2個のデフォルト引数が定義できてしまう為、片方のデフォルト引数だけが有効になるという問題が起こる事があります。(その場合はリンケージ前に値が決まっているのでファイル毎の引数が有効になります。) 次回は動的引数について見てみたいと思います。