300 第 7 章演算子の多重定義 が与えられるとコンストラクタが使用されることになる . 例えば , のように宣言することができるであろう : class complex{ double て e ′ im; public : operator 十 = ( complex ) ー complex ( double r ′ double 土 = 0 ) { て e = て一 クラス complex を次 1 爪 = 工・ friend complex ope て atO て十 ( complex, complex friend complex operator* ( complex, complex complex operator 十 = ( complex complex operator*= ( complex そして , complex 変数と整数定数を含む演算は合法なものとなるであろう . 整数定数は , 虚数部がゼロの complex と解釈される . 例えば , a=b*2 は , ティカルな演算であることが知られていたならば , 演算の集合に operator*= ( double ) 合にのみ , 定義する必要がある . 例えば , もし複素変数に double を乗算することがクリ り達成される効率の向上にそれを行うだけの価値があることが , 経験により示されている場 を意味する . さて , + のような演算子のさらに特別なバージョンは , 変換を避けることによ a operator* ( b, complex ( double ( 2 ) ′ double ( 0 ) ) friend complex operator* ( complex ′ complex 房 friend complex operator 十 ( complex ′ complex 房 complex ( double r, double 土 = 0 ) { re=r ー土 m = 土・ public : double re ′土爪ー class complex { を加えるかもしれない : complex& complex& complex& operator* operator* = ( complex ) : = ( double
160 第 4 章関数とファイル 10q ( double double exp ( double cos ( double sin(double); sqrt(double); / / <math. h > の部分集合 { / / 標準 math ライプラリは c プログラムであることが多い double double double double extern "C" ます , ソースファイルをコンパイルし , それらのオプジェクトファイルを作る . それか こで $ はシステムのプロンプトである . $ ranlib math . a $ ar cr math. a sqrt . 0 Sin. 0 COS .0 expr. 0 10q.0 $ CC —c sqrt . c sin. c cos . c expr. c 109. c math. a と呼ばれるライプラリは , 次のようにして作ることができる : にそれそれ収められている . そして , これらの関数の定義は , ファイル sqrt. c, sin. c, cos . c, exp ・ c, 10q ・ c $ CC myprog ・ c sqrt .0 cos ・ 0 と cos() だけを呼び出しているのであれば : 自明ではない . 上の例では , すべてを取り込んでいるが , myprog. c 内の関数が sqrt ( ) ほとんどのプログラムにとって , 正確に .0 ファイルの集合を見つけ出すことは , 明らかに $ CC myprog ・ c sqrt. 0 s in. 0 cos ・ 0 expr ・ 0 10q.0 のだろうか ? 例えば , 次を考えてみよう : さて , . 0 ファイルを直接使う代わりに math. a を使うことに , どのような利点がある $ CC myprog ・ c math. a ルで a ての項を探されたい . ライプラリは次のようにして利用できる : ranl 土 b というコマンドがなければ , おそらくそれを行う必要はない . 詳細は , マニュア 用できるように , そのアーカイプにインデックスを付ける . 読者の使っているシステムに ら , ar コマンドを使って math. a と呼ばれるアーカイプを作る . 最後に , より速く利
156 第 4 章関数とファイル token value curr tok; double number value ー char name string [ 256 token value get token ( ) { / ☆ 構文解析器のインタフェースは , 特にきれいである : double prim( double term( ) : double expr ( / / syn. h : 構文解析と評価の宣言 extern extern extern / / syn #include #include #include #include 構文解析と評価の定義 "table . h" syn ・ h" " 1ex . h " error double prim( ) { double term( ) { double expr ( ) { / ☆ / ☆ 主プログラムは , いつも通りの自明なものである : / / main. c : 主プログラム #include #include #include #include #include #include <iostream. h> " lex . h " ・ syn ・ h" "table . h" く string . h> int main(int argc ′ char* argv[ ] ) { / ☆ プログラムにどれだけの数のヘッダファイルを使うのかは , いろいろな要因の関数とし て決まる . これらの要因の多くは , c + + 自身よりも , システムのファイルの扱い方に関係
172 第 4 章関数とファイル 呼び出し print(0 ) は , 0 が int なので print(int) を起動する . print( 'a' ) は , 'a' が char なので print(char) を起動する . ( 2.5.2 ) 多重定義解決は考える関数の宣言の順序に依存せず , 関数の戻り値も考慮されないこと にラ意されたい . これらの規則が与えられると , 関係する型に応じて効率や精度が著しく異なるときでも , 最も単純なアルゴリズム ( 関数 ) が用いられることを保証することができる . 例えば : int pow( int, int double pow( double ′ double complex pow( double, complex pow( complex ′ complex pow( complex ′ complex pow( complex ′ void k( complex 2 ) complex int) ー double complex int 土 = pow( 2 ′ 2 房 double d = pow ( 2 . 0 ′ 2 complex 22 complex z3 complex 24 4.6.7 デフォルト引数 po ( 2 ′ 2 pow ( 2 po ( 2 ′ 2 / / <math . h> より / / <complex. h> より / / pow( complex ′ complex ) を起動 / / pow(complex ′ int) を起動 / / pow(double ′ complex) を起動 / / pow(double ′ double) を起動 / / pow(int,int) を起動 関数では , 最も単純な , そしてしばしば最も頻度の高い場合よりも , 一般的な場合の方 がより引数を必要とすることが多い . これは特にオプジェクトを構築する関数 ( 例えば , コンストラクタ , 5.2.4 を見られたい ) によく見られる . こういった関数は , 柔軟性のた めにオプションをいくつか提供することが多い . 整数を印字する関数を考えてみよう . 何 進法で印字するかのオプションをユーザに与えることは理にかなっているように思われる が , ほとんどのプログラムでは , 整数は 10 進整数値で印字される . void print(int valuer int base = 10 ) : void f ( ) print(31); print ( 31 ′ 10
298 第 7 章演算子の多重定義 できないことを保証する . 特に , ポインタについて排他的に演算を行う演算子関数を定義 することはできない . このことは , C + + は拡張陸はあっても ( クラスオプジェクトについ ての演算子 = , & , 及び′を例外として ) 変形可能ではない , ということを保証している . 最初のオペランドとして基本型を受け付けるように意図された演算子関数は , メンバ関 数にはなり得ない . 例えば , 整数 2 に複素数の変数 aa を加えることを考える . aa + 2 は , 適切に宣言されたメンバ関数から , aa ・ operator + (2 ) と解釈できるが , 2 + aa は解釈 できない . というのは , 2 . operator + (aa) を意味するように + を定義するためのクラ ス int が存在しないからである . たとえ存在したとしても , 2 + aa と aa + 2 をうまく処 理するためには , 二つの異なるメンバ関数が必要となる . コンバイラはユーザ定義の + の 意味を知らないため , それが可換であると仮定して 2 + aa を aa + 2 と解釈することはでき ない . この例はフレンド関数を用いれば自明に処理される . すべての式は曖昧さをチェックされる . ューザ定義演算子が可能な解釈を提供している 場合 , その式は阯 13.2 の規則に従って調べられる . 7.3 ユーサ定義型変換 .1 で示された複素数の実装は , 誰をも満足させるにはあまりに限定されており , 拡張 しなければならない . これは以前に示した技法のほとんど自明な繰り返しである . 例えば : class complex { double re, im; public: complex ( double friend complex friend complex friend complex friend complex friend complex friend complex て′ double i) { re operator 十 ( complex ′ operator 十 ( complex, operator 十 ( double ′ operator— ( complex ′ operator— ( complex ′ complex double complex complex); double operator— ( double ′ complex ) : complex operator— ( / / 単項の一 friend complex operator* ( complex ′ friend complex operator* ( complex ′ friend complex operator* ( double, complex double complex) :
8.5 テンプレート関数の多重定義解決 void f(int 土′ double d ′ complex 2 ) 363 complex 21 complex 22 complex 23 sqrt(i); sqrt(d); sqrt( 2 / / sqrt(int) / / sqrt(complex) / / sqrt(double) この例では , 三つの引数の型のそれそれに対してテンプレートから sqrt 関数が生成 される . もしユーザが何か違うこと一例えば , 引数として int が与えられたときの sqrt(double) の呼び出し一を望むならば , 明示的な型変換を使用しなければなら ない : template<class T> T sqrt(T); void f(int i ′ double d, complex 2 ) complex 21 complex 22 complex 23 / / sqrt ( double ) sqrt(double(i) / / sqrt ( double ) sqrt(d); / / sqrt ( complex ) sqrt(z こでは , sqrt(double) 及び sqrt(complex) の定義のみがテンプレートから生成 される . テンプレート関数は , その名前の他の関数か , もしくは同じ名前の他のテンプレート関 数のどちらかによって , 多重定義してもよい . 同じ名前を持つテンプレート関数や他の関 数についての多重定義解決は , 三段階で行われる * 国関数の正確な一致 ( 13.2 ) を捜す . 見つかったらそれを呼び出す . [ 2 ] 正確な一致で呼び出せる関数を生成できる関数テンプレートを捜す . 見つかった らそれを呼び出す . [ 3 ] 関数に対して通常の多重定義解決を試みる . 関数が見つかればそれを呼び出す . 致が見つからなければ , その呼び出しはエラーである . これらの規則は大変厳格であり , ポインタ変換やリファレンス変換 , そしてことによると他の標準的な変換も許す ように緩められることもありそうである . そうなった場合には , 曖昧さの制御がいつも通りに適用されるであろう .
756 r. 12 特殊なメンバ関数 complex a ( 1 complex b complex c complex d complex e ー complex f complex( 1 ′ 2 sqrt ( b ′ c リファレンスマニュアル / / complex(double) の呼び出し / / による初期言殳定 ′のコピーによる初期設定 complex ( double, double ) を / / 使って complex ( 1 ′ 2 ) を構築 し , それを・ c , にコピーする sqrt ( complex ′ complex ) を / / 呼び出し , その結果を , d ′に コピーする / / complex() の呼び出しによって / / 初期設定する complex(double) を使って / / comp1ex(3) を構築し , それを 吁′にコピーする 代入演算子 = の多重定義は , 初期設定には何の影響も与えない . 引数渡しや関数からの復帰時に発生する初期設定は , 以下の形式と同値である : new 式 ( 阯 5.3.3 ) や基底クラスやメンバの初期設定子 ( 12.6.2 ) で起こる初期設定は , 以下の形式と同値である : T x ( a ) : コンストラクタを持ったクラスのオプジェクトの配列の初期設定には , 単独のオプジェ クト同様にコンストラクタが使われる ( 12.1 ) . 初期設定並びの中に , 配歹腰素よりも 少ない初期設定子しかなかったならば , デフォルトコンストラクタ ( 12.1 ) が用いられ る . もし , デフォルトコンストラクタがないならば , 加ルイ ( 初期設定子並び ) が 揃っていなければならない . 例えば , コンストラクタを使う { 1 ′ 2 / / 工ラー complex CC { 1 ′ complex ( 1 ′ 2 ) ′ complex ( ) ′ 2 complex ▽ [ 6 ] こで , v[0] と v[3] は complex: :complex(double) を使って初期設定され , v[ 1 ] は complex: :complex(double'double) を使って初期設定され , そして , v[2], v[4], v[5] は complex::complex() を使って初期設定される . クラス M のオプジェクトがクラス x のメンバになれるのは , ( 1 ) M がコンストラクタを
433 class complex { double re ′土爪ー public : complex ( double r 0 ′ double 土 complex operator* ( complex, complex operator— ( complexr complex ope て atO て十 ( complex ′ double imag(complex& a) { friend double real(complex& a) { friend friend friend friend friend 10.2 出力 0 ) { re=r; 土 m = 土・ return a . re ー } return a. im; } complex); complex); complex); complex operator/ ( complex, complex 房 演算子 < ぐよ , 新しい型 c 。Ⅲ plex に対して次のように定義することができる : ostream& operator<< ( ostream&s ′ complex 2 ) return S くく′ ( ′ << real ( 2 ) << ′ そして組み込み型に対する << と全く同じように用いることができる . 例えば : main ( ) x ( 1 ′ 2 は X complex cout << ( 1 ′ 2 ) を出した . ユーザ定義型の出力演算子を定義するのに , クラス。 stream の宣言の改変は 必要なく , それによって保守される ( 隠された ) データ構造にアクセスすることもない . 前者は幸いである . というのはクラス ost て eam の宣言はほとんどのユーザが書き込みア クセス権を持たない標準ヘッダファイルの中にあり , もし彼らがアクセス権を持っていた としても改変したいとは思わないだろうからである . 後者もまた重要である . というのは
307 class real { public : operator double ( operator int ( ) ー int i double d = a; void g(real a) 1 d 工 d 7.4 リテラル a. double( a ・ int ( a. double( a ・ int ( これらの場合でも , 型の解析はなおポトムアップであり , 算子とその引数の型のみが考慮される . 7.4 リテラル どんなときを取っても一つの演 あるクラス型のリテラルを , 1 . 2 や 12e3 が型 double のリテラルであるという意味 で定義することは不可能である . しかしながら , クラスメンバ関数を用いてその解釈が提 供されていれば , 基本型のリテラルをしばしば代わりに用いることができる . 単一引数を 取るコンストラクタは , このための一般的なメカニズムを招共する . コンストラクタが単 純でありインラインで置き換えられるならば , コンストラクタの起動をリテラルとみな すことは極めて合理的である . 例えば , <complex. h> におけるクラス complex の宣 言が与えられたとして , 式 221 * 3 + 222 ☆ co Ⅲ plex ( 1 ′ 2 ) は五つではなく二つの関数呼 び出しを起こすだろう . 二つの☆演算は本当の関数呼び出しを起こすだろうが , + 演算と comp1ex(3) や co Ⅲ plex ( 1 ′ 2 ) を作るために呼ばれるコンストラクタはインライン展 開されるだろう .
36 第 1 章 C + + ひとめぐり int pow( int ′ int double double pow ( double ′ / / pow(int' int ) の呼び出し x = pow ( 2 ′ 10 房 / / pow( double' double ) の呼び出し Y = pow( 2 . 0 ′ 10 . 0 このような名前の多重使用は関数名の多重定義 (function-name overloading) , もしくは 簡単に多重定義 (overloading) と呼ぶ . 多重定義については , 第 7 章で議論する . 関数への引数は。値渡し " もしくは。切ファレンス渡し " のいずれかで渡すことができ る . 例えば , 二つの整数変数の値を交換する関数を書くことを考えよう . デフォルトの値 渡しを使う場合 , ポインタを使わなければならない : void swap(int* p ′ int* q) int t 単項演算子☆は間接参照演算子で , ポインタによって指されているオプジェクトの値を戻 す . この関数は , 次のように呼び出すことができる : void f ( int i ′ int j ) swap ( & i ′ & j リファレンス渡しを使うと , 明示的なポインタ操作を避けることができる : void swap(int& rl ′ int& r2) int t = て 1 : て 1 r2 void 9 ( int 土′ int j ) swap ( i ′ j ) :