Seaside Laboratory

Posts

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

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

条件演算子に使った独自の型を簡単に書くと以下のようになる。

// 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;
    }
};

int と double による初期化と値の取り出しができるだけの型。どの関数が呼ばれたか分かるようにデバッグ文が挿入してある。まずは試しに簡単な代入をして結果を確認。

// プログラム
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

条件演算子を使うときは同じ型に統一するのが無難か。