首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Effective C++ 條款 24/46:非成員函式支援混合模式運算與模板友元技巧

     編輯:關於C++

背景:非成員函式才能套用所有參數的隱式轉換

對於 Rational 類別,欲支援 oneHalf * 2 (混合模式),operator* 必須是非成員函式,否則 2 無法隱式轉換為 Rational。

模板化帶來的新挑戰:模板實參推演失敗

當 Rational 與 operator* 皆為模板:


template<typename T> class Rational { ... };
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs);

Rational<int> oneHalf(1,2); Rational<int> result = oneHalf * 2; 編譯失敗!原因:編譯器需推演 T,第一參數推得 T=int,但第二參數是 int,無法推演 T —— 模板實參推演不考慮建構函式隱式轉換。

解決之道:類別內宣告友元函式

在 Rational<T> 內宣告 operator* 為友元:


template<typename T>
class Rational {
public:
    // ...
    friend
    const Rational operator*(const Rational& lhs, const Rational& rhs);
};
// 類別外定義
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs) { ... }

關鍵點:

  1. Rational<int> oneHalf 實例化類別時,友元函式 operator*(const Rational<int>&, const Rational<int>&) 被自動宣告 (非模板)。
  2. 呼叫時編譯器看到具體函式,可套用 Rational<int> 的 non-explicit 建構函式將 2 轉換,混合模式運算成功。

連結錯誤與終極解法:內聯定義於類別內

僅宣告友元不定義會導致連結錯誤。最簡單修法:將實作直接寫在友元宣告內 (隱式 inline):


template<typename T>
class Rational {
public:
    // ...
    friend const Rational operator*(const Rational& lhs, const Rational& rhs) {
        return Rational(lhs.numerator() * rhs.numerator(),
                        lhs.denominator() * rhs.denominator());
    }
};

複雜函式可讓友元呼叫外部 helper 函式,減輕 inline 膨脹。

語法細節:類別模板內的名稱縮寫

在 Rational<T> 內可直接寫 Rational 代替 Rational<T>,簡化友元簽名。

反直覺但有效:友元非為存取私有成員

此處使用友元非為存取 private/protected,而是為了「在類別內宣告非成員函式」—— 這是 C++ 唯一能在類別內宣告非成員函式的語法。為了支援所有參數隱式轉換 (需非成員) + 模板實參推演失敗時的自動實例化 (需類別內宣告),友元成了最佳拍檔。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved