cpp-templates

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C++ Templates

C++ Templates

Purpose

用途

Guide agents through reading and fixing template error messages, using concepts as cleaner constraints, understanding SFINAE vs concepts trade-offs, and profiling template instantiation depth and compile times with Templight.
指导开发者解读并修复模板错误信息,使用concepts作为更简洁的约束方式,理解SFINAE与concepts的权衡,以及使用Templight分析模板实例化深度和编译时间。

Triggers

触发场景

  • "How do I read this massive C++ template error?"
  • "How do I use concepts to constrain a template?"
  • "What's the difference between SFINAE and concepts?"
  • "My templates make compilation very slow"
  • "How do I write a requires-clause?"
  • "How do I profile template instantiation times?"
  • "我该如何解读这冗长的C++模板错误?"
  • "我如何使用concepts约束模板?"
  • "SFINAE和concepts有什么区别?"
  • "我的模板导致编译速度极慢"
  • "我该如何编写requires子句?"
  • "我如何分析模板实例化时间?"

Workflow

工作流程

1. Reading template error messages

1. 解读模板错误信息

Template errors print full instantiation chains. Strategy: read from the bottom up.
text
prog.cpp:25:5: error: no matching function for call to 'sort'
  std::sort(v.begin(), v.end());
  ^~~~~~~~~
/usr/include/c++/13/bits/stl_algo.h:4869:5: note: candidate:
    template<class _RAIter>
    void std::sort(_RAIter, _RAIter)
note: template argument deduction/substitution failed:
prog.cpp:25:5: note: 'MyType' is not a valid type for this template
                             ^~~~~~~~
Rules for reading:
  1. Find the first error line (top of output) — that's your code
  2. Skip all the
    note:
    lines until you find "required from here" or "in instantiation of"
  3. The bottom of the stack shows the type that failed substitution
bash
undefined
模板错误会打印完整的实例化链。策略:从下往上阅读。
text
prog.cpp:25:5: error: no matching function for call to 'sort'
  std::sort(v.begin(), v.end());
  ^~~~~~~~~
/usr/include/c++/13/bits/stl_algo.h:4869:5: note: candidate:
    template<class _RAIter>
    void std::sort(_RAIter, _RAIter)
note: template argument deduction/substitution failed:
prog.cpp:25:5: note: 'MyType' is not a valid type for this template
                             ^~~~~~~~
阅读规则:
  1. 找到第一条错误行(输出顶部)——这是你的代码位置
  2. 跳过所有
    note:
    行,直到找到"required from here"或"in instantiation of"
  3. 栈底显示了替换失败的类型
bash
undefined

Limit backtrace depth to reduce noise

限制回溯深度以减少冗余信息

g++ -ftemplate-backtrace-limit=3 prog.cpp clang -ftemplate-depth=32 prog.cpp # default 1024
g++ -ftemplate-backtrace-limit=3 prog.cpp clang -ftemplate-depth=32 prog.cpp # 默认值为1024

Show simplified errors (GCC 12+)

显示简化错误(GCC 12+)

g++ -fconcepts-diagnostics-depth=3 prog.cpp # for concept failures
undefined
g++ -fconcepts-diagnostics-depth=3 prog.cpp # 针对concept失败场景
undefined

2. SFINAE — legacy constraint technique

2. SFINAE — 传统约束技术

SFINAE (Substitution Failure Is Not An Error) silently removes overloads that fail substitution:
cpp
#include <type_traits>

// Enable function only for arithmetic types
template <typename T,
    std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T square(T x) { return x * x; }

// SFINAE with return type
template <typename T>
auto to_string(T x) -> std::enable_if_t<std::is_integral_v<T>, std::string> {
    return std::to_string(x);
}

// Void-t technique for detecting member existence
template <typename, typename = void>
struct has_size : std::false_type {};

template <typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>>
    : std::true_type {};
SFINAE errors are cryptic. Prefer concepts (C++20) for new code.
SFINAE(Substitution Failure Is Not An Error,替换失败并非错误)会静默移除替换失败的重载:
cpp
#include <type_traits>

// 仅对算术类型启用该函数
template <typename T,
    std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
T square(T x) { return x * x; }

// 基于返回类型的SFINAE
template <typename T>
auto to_string(T x) -> std::enable_if_t<std::is_integral_v<T>, std::string> {
    return std::to_string(x);
}

// 用于检测成员是否存在的Void-t技术
template <typename, typename = void>
struct has_size : std::false_type {};

template <typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>>
    : std::true_type {};
SFINAE的错误信息晦涩难懂。新代码优先使用C++20的concepts。

3. Concepts — modern constraints (C++20)

3. Concepts — 现代约束方式(C++20)

cpp
#include <concepts>

// Define a concept
template <typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

template <typename T>
concept Printable = requires(T x) {
    { std::cout << x } -> std::same_as<std::ostream&>;
};

template <typename T>
concept Container = requires(T c) {
    c.begin();
    c.end();
    c.size();
    typename T::value_type;
};

// Apply concept as constraint
template <Arithmetic T>
T square(T x) { return x * x; }

// Abbreviated function template (C++20)
auto square(Arithmetic auto x) { return x * x; }

// requires-clause (more complex conditions)
template <typename T>
    requires Arithmetic<T> && (sizeof(T) >= 4)
T big_square(T x) { return x * x; }

// Concept in auto parameter
void print_container(const Container auto& c) {
    for (const auto& elem : c) std::cout << elem << ' ';
}
cpp
#include <concepts>

// 定义一个concept
template <typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

template <typename T>
concept Printable = requires(T x) {
    { std::cout << x } -> std::same_as<std::ostream&>;
};

template <typename T>
concept Container = requires(T c) {
    c.begin();
    c.end();
    c.size();
    typename T::value_type;
};

// 将concept作为约束应用
template <Arithmetic T>
T square(T x) { return x * x; }

// 简化函数模板(C++20)
auto square(Arithmetic auto x) { return x * x; }

// requires子句(更复杂的条件)
template <typename T>
    requires Arithmetic<T> && (sizeof(T) >= 4)
T big_square(T x) { return x * x; }

// 自动参数中的concept
void print_container(const Container auto& c) {
    for (const auto& elem : c) std::cout << elem << ' ';
}

4. Requires expressions

4. Requires表达式

cpp
// requires { expression; } — checks expression is valid
// requires { expression -> type; } — checks type of expression

template <typename T>
concept HasPush = requires(T c, typename T::value_type v) {
    c.push_back(v);                          // must be valid
    { c.front() } -> std::same_as<typename T::value_type&>;  // type check
    { c.size() } -> std::convertible_to<std::size_t>;        // convertible
    requires std::default_initializable<T>;  // nested requirement
};

// Compound requires (all must hold)
template <typename T>
concept Sortable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a == b } -> std::convertible_to<bool>;
};
cpp
// requires { expression; } — 检查表达式是否有效
// requires { expression -> type; } — 检查表达式的类型

template <typename T>
concept HasPush = requires(T c, typename T::value_type v) {
    c.push_back(v);                          // 必须有效
    { c.front() } -> std::same_as<typename T::value_type&>;  // 类型检查
    { c.size() } -> std::convertible_to<std::size_t>;        // 可转换
    requires std::default_initializable<T>;  // 嵌套要求
};

// 复合requires(所有条件必须满足)
template <typename T>
concept Sortable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a == b } -> std::convertible_to<bool>;
};

5. SFINAE vs concepts comparison

5. SFINAE与concepts对比

AspectSFINAEConcepts
SyntaxComplex, verboseClean, readable
Error messagesCryptic wall-of-textClear constraint failure
Compile timeCan be slow (many substitutions)Generally faster
C++ versionC++11C++20
Short-circuitNoYes (concept subsumption)
Use in
if constexpr
AwkwardNatural
Overload rankingManually via priorityAutomatic by constraint specificity
Migration: replace
enable_if
with concept constraints; replace
void_t
helpers with
requires
.
维度SFINAEConcepts
语法复杂、冗长简洁、易读
错误信息晦涩的长篇文本清晰的约束失败提示
编译时间可能较慢(多次替换)通常更快
C++版本C++11C++20
短路特性有(concept包含)
if constexpr
中的使用
繁琐自然
重载优先级手动设置优先级按约束特异性自动排序
迁移建议:用concept约束替换
enable_if
;用
requires
替换
void_t
工具类。

6. Template instantiation profiling with Templight

6. 使用Templight分析模板实例化

bash
undefined
bash
undefined

Install Templight (Clang-based profiler)

安装Templight(基于Clang的分析器)

Build with Templight tracing

启用Templight追踪进行构建

clang++ -Xtemplight -profiler -Xtemplight -memory
-std=c++17 prog.cpp -o prog
clang++ -Xtemplight -profiler -Xtemplight -memory
-std=c++17 prog.cpp -o prog

Convert trace to visualizable format

将追踪结果转换为可视化格式

templight-convert -f callgrind -o prof.out templight.pb
templight-convert -f callgrind -o prof.out templight.pb

View with KCachegrind

使用KCachegrind查看结果

kcachegrind prof.out
kcachegrind prof.out

Find top template instantiation costs (without Templight)

(不使用Templight)查找模板实例化成本最高的部分

ClangBuildAnalyzer (easier)

ClangBuildAnalyzer(更简便)

ClangBuildAnalyzer --start /tmp/build cmake --build build ClangBuildAnalyzer --stop /tmp/build capture.bin ClangBuildAnalyzer --analyze capture.bin | head -50
undefined
ClangBuildAnalyzer --start /tmp/build cmake --build build ClangBuildAnalyzer --stop /tmp/build capture.bin ClangBuildAnalyzer --analyze capture.bin | head -50
undefined

7. Reducing template compile times

7. 减少模板编译时间

cpp
// 1. Explicit instantiation — compile once, use everywhere
// header.h
template <typename T>
T transform(T x);

extern template int transform<int>(int);    // suppress instantiation

// impl.cpp
#include "header.h"
template int transform<int>(int);           // instantiate here only

// 2. Prefer function templates over class templates when possible
// (functions instantiate lazily; class templates instantiate eagerly)

// 3. Use concepts to short-circuit failed substitutions
// (concept check is faster than full substitution failure)

// 4. Split heavy template headers from lightweight ones
// - Put type definitions in forward_decls.h
// - Put template implementations in impl.h (include only where needed)

// 5. Use if constexpr instead of specialization
template <typename T>
void process(T x) {
    if constexpr (std::is_integral_v<T>) {
        handle_int(x);
    } else {
        handle_other(x);
    }
}
cpp
// 1. 显式实例化 — 编译一次,多处使用
// header.h
template <typename T>
T transform(T x);

extern template int transform<int>(int);    // 抑制实例化

// impl.cpp
#include "header.h"
template int transform<int>(int);           // 仅在此处实例化

// 2. 尽可能优先使用函数模板而非类模板
// (函数模板延迟实例化;类模板立即实例化)

// 3. 使用concepts提前终止失败的替换
// (concept检查比完整替换失败更快)

// 4. 将重型模板头文件与轻量级头文件分离
// - 类型定义放在forward_decls.h中
// - 模板实现放在impl.h中(仅在需要时包含)

// 5. 使用if constexpr而非特化
template <typename T>
void process(T x) {
    if constexpr (std::is_integral_v<T>) {
        handle_int(x);
    } else {
        handle_other(x);
    }
}

Related skills

相关技能

  • Use
    skills/build-systems/build-acceleration
    for ccache and PCH to reduce overall compile time
  • Use
    skills/compilers/clang
    for Clang-specific diagnostics and concept error output
  • Use
    skills/low-level-programming/cpp-coroutines
    for another advanced C++20 feature
  • 使用
    skills/build-systems/build-acceleration
    通过ccache和PCH减少整体编译时间
  • 使用
    skills/compilers/clang
    获取Clang特定的诊断信息和concept错误输出
  • 使用
    skills/low-level-programming/cpp-coroutines
    了解另一项C++20高级特性