ikautak.log

C/C++, Python, CUDA, Android, Linux kernel, Network, etc.

More Effective C++ 項目5 ユーザ定義の変換関数に気をつけよう

新訂版 More Effective C++ (AddisonーWesley professional co)

新訂版 More Effective C++ (AddisonーWesley professional co)

  • 作者: スコット・メイヤーズ,安村通晃,伊賀聡一郎,飯田朱美,永田周一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/06/29
  • メディア: 単行本(ソフトカバー)
  • 購入: 8人 クリック: 129回
  • この商品を含むブログ (43件) を見る

項目5 ユーザ定義の変換関数に気をつけよう

C++はCと同様、コンパイラがcharからintなど暗黙の型変換を行う。
コンパイラに暗黙の型変換を行わせるような関数が2種類ある。

単一の実引数のコンストラクタ

単一の引数を持つか、複数の引数で2番目以降がデフォルト引数を持つものとして宣言する。
以下の例ではstringからNameに変換可能になる。

class Name {
public:
    Name(const string& s);
    ...
};
暗黙の型変換演算子
class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);
    ...
    operator double() const;  // Rationalをdoubleに変換する
};

このようにすると、doubleに変換できる。

Rational r(1, 2);
double d = 0.2 * r;  // doubleに変換される


だが、予想外のところでも変換関数が使われるようになる。

cout << r;  // double()が使われる。

explicitを使うと暗黙の型変換ができなくなる。

template<class T> class Array {
public:
    ...
    explicit Array(int size);
    ...
};

Array<int> a(10);  // intなのでOK
Array<int> b(10);

if (a == b[i]) ...  // b[i]を使ってArray<int>を作り、aとの比較をしようとするが、
                    // explicitなので暗黙にintからArray<int>に変換できない。

内部にクラスを作った場合

template<class T> class Array {
public:
    class ArraySize {
    public:
        ArraySize(int numElements) : theSize(numElements) {}
        int size() const { return theSize; }

    private:
        int theSize;
    };

    Array(int lowBound, int highBound);
    Array(ArraySize size);
    ...
};

このとき

Array<int> a(10);

と書くと、コンパイラはintを引数にとるArrayコンストラクタを探すが、そのコンストラクタはない。
コンパイラはArraySizeがintから作れることを知っているので、テンポラリのArraySizeオブジェクトを作り、Arrayを作る。
そうすると次のように比較だけはエラーにできる。

Array<int> a(10);
Array<int> b(10);

if (a == b[i]) ...  // aと比較するため右辺にArray<int>オブジェクトが必要だが
                    // intからArraySizeを作り、それからArray<int>を作るのは
                    // 二つの変換関数を呼ぶ必要があるが、エラーになる

ArraySizeクラスはプロキシクラスと呼ばれることが多い。

まとめ
コンパイラの暗黙の型変換は通常は利点より災難をもたらすので、変換関数は用意しないほうがよい。