cpp-templates

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cpp-templates" with this command: npx skills add mohitmishra786/low-level-dev-skills/mohitmishra786-low-level-dev-skills-cpp-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.

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?"

Workflow

  1. Reading template error messages

Template errors print full instantiation chains. Strategy: read from the bottom up.

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:

  • Find the first error line (top of output) — that's your code

  • Skip all the note: lines until you find "required from here" or "in instantiation of"

  • The bottom of the stack shows the type that failed substitution

Limit backtrace depth to reduce noise

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

Show simplified errors (GCC 12+)

g++ -fconcepts-diagnostics-depth=3 prog.cpp # for concept failures

  1. SFINAE — legacy constraint technique

SFINAE (Substitution Failure Is Not An Error) silently removes overloads that fail substitution:

#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.

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

#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 << ' '; }

  1. Requires expressions

// 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>; };

  1. SFINAE vs concepts comparison

Aspect SFINAE Concepts

Syntax Complex, verbose Clean, readable

Error messages Cryptic wall-of-text Clear constraint failure

Compile time Can be slow (many substitutions) Generally faster

C++ version C++11 C++20

Short-circuit No Yes (concept subsumption)

Use in if constexpr

Awkward Natural

Overload ranking Manually via priority Automatic by constraint specificity

Migration: replace enable_if with concept constraints; replace void_t helpers with requires .

  1. Template instantiation profiling with Templight

Install Templight (Clang-based profiler)

https://github.com/mikael-s-persson/templight

Build with Templight tracing

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

View with KCachegrind

kcachegrind prof.out

Find top template instantiation costs (without Templight)

ClangBuildAnalyzer (easier)

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

  1. Reducing template compile times

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

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cmake

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

static-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

llvm

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gdb

No summary provided by upstream source.

Repository SourceNeeds Review