隨著閱讀進度的推進,愈發能感受到概念的複雜與深奧。若遇到不理解的部分,請隨時回顧前面的文章,一起在後續的學習中更游刃有餘!
如知識點 15 所述,C++ 支持衍生類別指標隱式轉換為基底類別指標。但在模板類中,可能會發生 SmartPtr<Student>
與其衍生類別 SmartPtr<Yoyo>
為不同類型。即使 Student 與 Yoyo 在繼承關係中,模板實例之間並無繼承關係。
成員函式模板可模仿智慧指標間的隱式型別轉換。例如透過 template<typename Yoyo>
泛型建構函式實現:
template <typename Student>
class SmartPtr {
public:
explicit SmartPtr(Student* realPtr) {}
template<typename Yoyo>
SmartPtr(const SmartPtr<Yoyo>& other) : heldPtr(other.get()) {}
};
然而,這種設計可能允許無效的不合法類型轉換,例如 SmartPtr<double>
和 SmartPtr<int>
互相轉換。因此需利用原始 C++ 的非模板型別隱式轉換進行約束,即當無法隱式轉換時應回報編譯錯誤。使用成員函式模板實現所有兼容的型別轉換,仍需顯式提供預設非模板的建構函式和賦值函式,以避免不必要的行為偏差或預期外的情況。
順帶一提,範例中使用的「初始化列表」在物件建立時就直接初始化成員變量。而「賦值」則是在物件建立後,透過賦值運算子進行操作。初始化列表通常效率較高,特別是在處理參考、const
成員或 class
成員時。
一般情況下,非模板類提供非成員函式以支持混合型別。但若使用模板類,在 type deduction 階段不會考慮隱式轉型函式,導致 RationalTP<int> * int
等混合型別操作無法編譯。
將非成員函式聲明為模板類的 friend
函式即可解決。其在模板類中自動宣告並生成具體型別,此時隱式型別轉換函式得以生效。知識點 19 有討論到相關的成員訪問權限設計。
template <typename T>
class RationalTP {
public:
RationalTP(const T& numerator = 0, const T& denominator = 1)
: m_numerator(numerator), m_denominator(denominator) {}
const T numerator() const { return m_numerator; }
const T denominator() const { return m_denominator; }
friend const RationalTP operator*(const RationalTP &lhs, const RationalTP &rhs) {
return RationalTP(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}
private:
T m_numerator;
T m_denominator;
};