Version 16.16
演算子のオーバーロードとオペランド
「前回は演算子のオーバーロードについて復習しました」
『っていうか、カンマのオーバーロードって変すぎるんだけど!』
「これだね」
// , 演算子のオーバーロードメンバ関数。
CData & CData::operator , ( int p_i )
{
// 引数をメンバ変数にセットします。
m_iData = p_i;
// 自分の参照を返します。
return *this;
}
『っていうかこれ作れって言われても絶対作れないんだけど』
「そうでもないよ。演算子のオーバーロードには、ちゃんとしたルールがあ
るんです」
『え、ルールが? それ憶えてればどんなのも作れるってこと?』
「そういうこと。まず、演算子には単項演算子と二項演算子がある、ってい
うのを思い出して」
『単項演算子は Version 6.09 ( No.109 ) で、二項演算子は
Version 6.10 ( No.110 ) でやったね』
「まずは二項演算子に限定して説明します。二項演算子は、演算子を挟んで
ふたつの値を指定します。この値のことを【オペランド】と言いました」
『うん、たとえば』
m_iData = p_i;
『だったら』
・左オペランド:m_iData
・右オペランド:p_i
『なんだよね』
「そうそう。これは、全ての二項演算子で同様です。【,】であってもね」
『そういえばそうだ……』
「さて、二項演算子をオーバーロードする場合、メンバ関数は以下のように
なります」
戻り値の型 左オペランドの型::operator 演算子 ( 右オペランドの型 )
『左オペランドと右オペランド……』
「たとえば、以下のように ,演算子をオーバーロードしたい場合」
cData , 100;
「左オペランドは CData クラス、右オペランドは int 型になるから、以下
のようになります」
戻り値の型 CData::operator , ( int p_i )
『あ、こういうふうに決まるんだ!』
「まずは使い方を決めて、そこから、左オペランドを自クラス、
右オペランドを引数ってすればいいわけ」
『あれ? でもこれって CData クラスのメンバ関数だよね。左オペランド
が CData クラスじゃない場合にはどうするの?』
「そのクラスのメンバ関数にすればいいってこと。つまり、左オペランドの
クラスのメンバ関数にして、右オペランドは引数に、ってすればいいってこ
と」
『? それはそうなんだけど……たとえばさ、左右逆に』
100 , cData;
『ってしたい場合にはどうするの?』
「その場合には、ちょっとルールが変わります。左オペランドのクラスの
メンバ関数にする、っていうルールは変わらないから、こういうふうに
int 等の基本型が左オペランドになる場合には、メンバ関数としては作成で
きません」
『メンバ関数としてはできない……ってことは、普通の関数としてはでき
る、ってこと?』
「そういうこと。メンバ関数ではない、普通の関数の場合、以下のルールに
なります」
戻り値の型 operator 演算子 ( 左オペランドの型, 右オペランドの型 )
『あ、メンバ関数じゃなくなったら、引数が2つに増えた』
「普通の関数の場合、左オペランドが第1引数、右オペランドが第2引数にな
ります」
『なんか、前に言ってた、 this が隠し引数になっている、って話に似て
るかも』
「 Version 16.03 ( No.330 ) で説明したことだね。確かにちょっと似てる
かも。この普通の関数の方が基本で、メンバ関数版は、第1引数を this に
した、って考えると分かりやすいかもね」
『うん、そうかも』
「最初の例も含めて、メンバ関数ではなく普通の関数にした場合には、以下
のようになります」
// Data.h
// CDataクラス。
class CData
{
public:
// private メンバ変数。
int m_iData;
};
// , 演算子のオーバーロードメンバ関数。
CData & operator , ( CData &p_rcData, int p_i );
CData & operator , ( int p_i, CData &p_rcData );
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// , 演算子のオーバーロードメンバ関数。
CData & operator , ( CData &p_rcData, int p_i )
{
p_rcData.m_iData = p_i;
return p_rcData;
}
CData & operator , ( int p_i, CData &p_rcData )
{
p_rcData.m_iData = p_i;
return p_rcData;
}
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// CData クラスの変数を宣言します。
CData cData;
// ,演算子のオーバーロードメンバ関数を使用します。
cData , 100;
100 , cData;
return 0;
}
『あれ! メンバ関数じゃなくてもいいんだ!』
「別にメンバ関数でもメンバ関数じゃなくてもいいんです。便利な方を選択
すればいいわけ。クラスによってはメンバ関数化できないこともあるしね」
『んー、メンバ関数化できないのは分かるけど、今回みたいな場合にはどっ
ちがいいの?』
「演算子によるかな。たとえば、 = 演算子の場合にはメンバ関数の方がいい
と思うよ。自クラスが右オペランドにくることはないし、何よりメンバ関数
なら private メンバ変数にアクセスできるから」
『そっか、 = 演算子だと代入だから、 private メンバ変数にアクセスでき
る方が便利なんだ』
「 + 演算子の場合には、普通の関数がいいかな。 + 演算子はオペランドに
変更を加えないし、ほとんどの場合左右のオペランドを入れ替えたものが必
要になるから、片方だけメンバ関数っていうよりは両方とも普通の関数の方
がいいだろうし」
『それが今回の , 演算子の例ね』
「そういうこと。こういう使い分けが必要かな」
『使い分け……質問!』
「はい火美ちゃん」
『両方あっちゃだめ? メンバ関数版と普通の関数版、こんな感じに』
// , 演算子のオーバーロードメンバ関数。
CData & CData::operator , ( int p_i )
{
// 引数をメンバ変数にセットします。
m_iData = p_i;
// 自分の参照を返します。
return *this;
}
CData & operator , ( CData &p_rcData, int p_i )
{
p_rcData.m_iData = p_i;
return p_rcData;
}
「そうすると、どっちを使えばいいのか分からないからコンパイルエラーに
なるんです」
cData , 100;
// コンパイルエラー:
// error C2593: 'operator ,' があいまいです。
『なるほど、確かにあいまいだよね』
「演算子のオーバーロードは、ルールが厳しいようで、実はいろんな表記方
法があるんです。その分、こういう曖昧さを生む可能性があるから、慣れて
くるとその解決の方が難しいかも」
/*
Preview Next Story!
*/
『うん、なんか演算子のオーバーロードが使いやすくなったかも』
「こういうルールが分かれば難しくないかもね」
『……でも難しいとこ多いけど。参照を使う所とか』
「演算子のオーバーロードと参照は切っても切れない関係なんです」
『切っても切れない?』
「というわけで次回」
< Version 16.17 演算子のオーバーロードの戻り値の型 >
『につづく!』
「オーバーロードのために参照が作られたとか?」
『そこまでは言わないけど……いや、もしかしたら……』