Seaside Laboratory

Posts

C++ の条件演算子 (三項演算子) と型変換

独自に作成した型を条件演算子に使ったら想定と違う挙動になってしまい、原因を突き止めるのに時間がかかってしまった。

まずは、int と double による初期化と値の取り出しだけを行う独自の型を定義。どの関数が呼ばれたのか分かるよう、関数名を出力するデバッグコードが入っている。

// double 型をラップしただけのクラス
class mydouble
{
    double m_value;

public:
    mydouble( int n ) : m_value( n )
    {
        puts( "mydouble( int n )" );
    }

    mydouble( double f ) : m_value( f )
    {
        puts( "mydouble( double f )" );
    }

    operator int ()
    {
        puts( "operator int ()" );
        return this->m_value;
    }

    operator double ()
    {
        puts( "operator double ()" );
        return this->m_value;
    }
};

試しに以下のような単純な代入式を書いて結果を確認する。

mydouble d1 = 12;
mydouble d2 = 3.4;
[出力結果]
mydouble( int n )
mydouble( double f )

型に応じたコンストラクタが呼び出されていて特に問題はない。

今度はこの型を条件演算子に組み込む。

mydouble d1 = 1.2;
mydouble d2 = true ? d1 : 34;
[出力結果]
mydouble( double f ) // d1 コンストラクタ
operator int () // d1 operator int
mydouble( int n ) // d2 コンストラクタ

後半の二行が条件演算子による出力になるが、式の結果が int として解釈されてしまったので d1 の operator int が呼び出され、その値を代入するために d2 の int 版コンストラクタが呼び出されている。

mydouble は浮動小数を表す型なので無意識のうちに、

mydouble d2 = true ? 1.2 : 34;

と脳内展開されていたが実際は、

mydouble d2 = true ? 1 : 34;

と解釈され、小数部が切り捨てられていた。

ちゃんと C++ の仕様を確認してみようということで、「プログラミング言語 C++」の算術変換の項を見てみると、以下の様に書かれている。

・被演算子のどちらかの型が long double なら、もう片方の型も long double に変換される。
・それ以外の場合で、被演算子のどちらかの型が double なら、もう片方の型も double に変換される。
・それ以外の場合で、被演算子のどちらかの型が float なら、もう片方の型も float に変換される。
・それ以外の場合、両方の被演算子に対して整数の格上げが実行される。

mydouble はこれらの条件にほぼ当てはまらず、最後にある「整数の格上げ」が適用されていた模様。

クラスがどの型になるのか判別できていないことが原因なので、組み込み型だけを使って書くと違った結果になる。

double d1 = 1.2;
double d2 = true ? d1 : 34;
printf( "%f\n", d2 );
[出力結果]
1.200000

条件演算子を使うときは同じ型に統一しておくのが無難か。