自訂型態轉換


在〈運算子重載〉,若 Rational 加法的左運算元是 int 整數的話,運算子重載時使用了 friend 非成員函式,這明確地定義了遇到 int 為左運算元,而右運算元為 Rational,計算結果要是 Rational 的話,應該採取的行為。

然而,在其他的運算需求中,可能會想要 Rational 能轉換為 intdouble 或者是其他型態,以便進一步以該型態的其他值進行運算,這可以透過自訂轉換函式來達到,又稱為轉型運算子。例如:

#include <iostream>
#include <string>
using namespace std;

struct Double {
    const double n;
    Double(double n) : n(n) {}
};

class Rational {
    int numer;
    int denom;

public:
    Rational(int numer, int denom) : numer(numer), denom(denom) {}

    operator double() {
        return static_cast<double>(this->numer) / this->denom;
    }

    operator Double() {
        return Double(static_cast<double>(this->numer) / this->denom);
    }
};

void foo(Double d) {
    cout << d.n << endl;
}

int main() {
    Rational a(1, 2);

    // a 隱含地轉換為 double
    cout << a + 0.1 << endl;  
    cout << 0.3 + a << endl;

    // a 隱含地轉換為 Double
    foo(a);

    return 0;
}

以上的範例,允許編譯器隱含地完成型態轉換,如果型態轉換必須得明確,可以加上 explicit,例如:

#include <iostream>
#include <string>
using namespace std;

struct Double {
    const double n;
    explicit Double(double n) : n(n) {}
};

class Rational {
    int numer;
    int denom;

public:
    Rational(int numer, int denom) : numer(numer), denom(denom) {}

    explicit operator double() {
        return static_cast<double>(this->numer) / this->denom;
    }

    explicit operator Double() {
        return Double(static_cast<double>(this->numer) / this->denom);
    }
};

void foo(Double d) {
    cout << d.n << endl;
}

int main() {
    Rational a(1, 2);

    cout << static_cast<double>(a) + 0.1 << endl;
    cout << 0.3 + static_cast<double>(a) << endl;

    foo(static_cast<Double>(a));

    return 0;
}

將範例中的 static_cast 拿掉,就會發生編譯錯誤,因為 explicit 指出不允許隱含型態轉換。