Foundations — SFINAE — substitution failure is not an error
This page assumes you have seen none of the notation in the parent note. We build every piece — template, typename, T, ::, decltype, ..., enable_if, _v, _t — from the ground up, in an order where each idea only uses ideas already explained.
0. The stage: what is a function overload?
Before any templates, we need the word overload.
Picture a wall of labelled mailboxes, all named print, each accepting a different shape of letter. You drop a letter in; the post office decides which box it belongs to.

1. template <typename T> — a recipe with a blank
template <typename T> // "for some type T ..."
T twice(T x) { // ... here is a function that doubles x
return x + x;
}Tis not a real type. It is a placeholder.- The real function does not exist until you call
twicewith something concrete.
You need templates because SFINAE is a rule about templates only. Non-template functions have no blanks to fill, so there is nothing to "substitute" and nothing to silently drop. See Templates and Type Deduction for how T gets guessed from the call.
2. Substitution & deduction — filling the blank
Two words that sound similar but are different steps.

3. The signature vs the body — WHERE the paste is watched
This is the single most important distinction on the whole topic.
Back to the résumé image from the parent: a broken header gets the résumé binned quietly (SFINAE); a fine header but nonsense spoken inside the interview (body) enrages the boss (hard error). The signature is the header; the body is the interview.
4. The :: operator — reaching inside a type
std::vector<int>::value_type // the "value_type" that lives inside vector<int> → int
int::value_type // ??? int has no member value_type → NONSENSEThis is why the parent's very first example (typename T::value_type) fails for T = int: after substitution the label tries to open a drawer that does not exist.
5. decltype(expr) — "ask the type of an expression without running it"
Why do we need a special tool for this, instead of just writing the type? Because with templates we don't know the type — it depends on the blank T. Only the compiler, after substitution, knows what t.size() returns. decltype lets us say "whatever that turns out to be."

See decltype and Trailing Return Types for how auto f(...) -> decltype(...) writes the return type after the parameters (so the parameter names like t are already in scope to be tested).
6. The comma operator inside decltype
The parent writes decltype(t.size(), size_t{}). Two things separated by a comma — what does that mean?
So decltype(t.size(), size_t{}):
- checks that
t.size()is a legal expression (validity gate → SFINAE), - but the resulting type is
size_t{}'s type =size_t.
7. ... — the variadic "catch-all" parameter
8. std::enable_if, _t, _v — the switch
Now we assemble the previous pieces into the modern tool.
Combine with :: (Section 4): writing enable_if_t<false, R> tries to open the ::type drawer of a type that has no such drawer → nonsense in the label → SFINAE drops the overload.

See std::enable_if and Tag Dispatch for how this is used to split overloads, and Concepts (C++20) for the readable modern replacement of this whole machinery. The void_t Detection Idiom uses the same "make a drawer appear or vanish" trick with std::void_t.
9. How the pieces feed the topic
Equipment checklist
Cover the right side and test yourself. If any answer is fuzzy, re-read that section before the main note.
What is an overload set, and what step picks one member of it?
What does template <typename T> create, and what is T?
T is a placeholder for "some type we don't yet know."Deduction vs substitution — which comes first and what does each do?
T from the call), then substitution (paste that T into the signature).Where must nonsense appear for SFINAE (not a hard error) to apply?
What does A::B mean, and what happens if B doesn't exist in A?
B living inside A"; if it's missing, the type is invalid — a substitution failure.What does decltype(expr) give you, and does it run expr?
expr would produce; it does NOT run the expression.In decltype(t.size(), size_t{}), what does the comma do?
t.size() for validity (SFINAE gate) but makes the overall type size_t.Why is a (...) parameter the ideal SFINAE fallback?
What do the suffixes _t and _v mean?
_t = "the ::type member of ..."; _v = "the ::value (bool/number) of ...".When does enable_if_t<Cond, R> become a substitution failure?
Cond is false, because the ::type drawer does not exist, and naming a missing member is nonsense in the label.Connections
- ↑ Back to the main SFINAE note
- Templates and Type Deduction
- Overload Resolution
- decltype and Trailing Return Types
- Type Traits (std::is_integral, std::void_t)
- std::enable_if and Tag Dispatch
- void_t Detection Idiom
- Concepts (C++20)