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();
    }
}

只有选中的 return 语句会被实例化,因此整数不需要 size() 成员。条件本身和非依赖代码仍必须是良构的。递归特化仍适用于归纳式类型计算,或必须支持旧版语言标准的代码,但不应以其掩盖直接的 constexpr 表达式。

地址工具与概念 #

即使 operator& 已被重载,std::addressof(object) 也能取得对象的实际地址。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 上阅读或评论(发表评论需要登录)