C++ 樣板元程式設計與型別特徵

最後更新: 2021-05-24 02:08:49 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

樣板元程式設計會在轉譯期間選擇型別與值。標準型別特徵函式庫提供常用的建構元件;相較於遞迴的類別樣板特化,constexpr、if constexpr 與概念能更直接地表達許多決策。

特徵、轉換與述詞 #

依照慣例,特徵會公開巢狀的 type 或 value。以 _t 結尾的標準別名會擷取型別,而以 _v 結尾的變數樣板則會擷取布林值或其他值。

cpp
#include <concepts>
#include <type_traits>

template <class T>
using storage_type = std::remove_cvref_t<T>;

template <class T>
inline constexpr bool is_plain_object_v =
    std::same_as<T, std::remove_cvref_t<T>>;

應有意識地使用轉換來正規化介面:

  • std::remove_cv_t、std::remove_reference_t 與 std::remove_cvref_t 會移除限定條件,以便比較型別。
  • std::decay_t 還會執行陣列至指標及函式至指標的調整,與以值傳遞引數的行為一致。
  • std::conditional_t 可在兩個型別之間選擇,而不必定義個別的特化。
  • std::common_type_t 會依其指定的轉換規則計算共同型別;它不能取代領域專屬的單位或精度政策。

std::is_integral_v、std::is_same_v 與 std::is_invocable_v 等述詞會回答結構性問題,但不會確立語意行為。

編譯期分支 #

當分支取決於樣板引數時,請使用 if constexpr。遭捨棄的相依分支不會被具現化,因此其中可以包含所選型別不支援的操作。

cpp
template <class T>
auto measure(const T& value) {
    if constexpr (std::is_integral_v<T>) {
        return value;
    } else {
        return value.size();
    }
}

只有被選取的回傳陳述式會被具現化,因此整數不需要 size() 成員。條件本身與非相依程式碼仍必須具備良好形式。對於歸納式型別計算,或必須支援較舊語言標準的程式碼,遞迴特化仍然適用,但不應讓它掩蓋直接的 constexpr 運算式。

位址工具與概念 #

std::addressof(object) 會取得物件的實際位址,即使 operator& 已被多載亦然。std::to_address(pointer) 會取得原始指標或類指標值所代表的原始位址;它不會驗證存留期、是否為空值或能否解除參照。

應使用概念來約束必要操作與結果特性,因為行為屬於介面契約:

cpp
#include <concepts>
#include <cstddef>
#include <functional>

template <class T>
concept Hashable = requires (const T& value) {
    { std::hash<T>{}(value) } -> std::convertible_to<std::size_t>;
};

特徵仍適合用於轉換與選擇加入的中繼資料。概念更能表達用戶端可執行的操作,進而讓多載解析與診斷反映該意圖。

評論

在 GitHub 上閱讀或評論(發表評論需要登入)