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は、個別の特殊化を定義せずに2つの型から一方を選択する。
  • 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();
    }
}

選択されたreturn文だけがインスタンス化されるため、整数にsize()メンバーは必要ない。一方、条件自体と非依存コードは、依然として適格な形式でなければならない。帰納的な型計算や、古い言語規格をサポートする必要があるコードには、再帰的特殊化が引き続き適している。ただし、直接的なconstexpr式を再帰的特殊化で分かりにくくするべきではない。

アドレスユーティリティとコンセプト #

std::addressof(object)は、operator&がオーバーロードされている場合でもオブジェクトの実際のアドレスを取得する。std::to_address(pointer)は、生ポインターまたはポインターのような値が表す生アドレスを取得する。オブジェクトの生存期間、nullかどうか、デリファレンス可能かどうかは検証しない。

振る舞いはインターフェース契約に属するため、必要な操作と結果の性質を制約するにはコンセプトを使用する。

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 で閲覧・コメントする(投稿にはログインが必要です)