Also why does the first template+class declaration is lacking "< S...>" right after the struct declaration?(see what's commented out)? when it's right to add it and when it's not?
It seems to me that is better to start from this point.
First of all, the following (removed the <S...> commented) is a declaration (attention: declaration only, not definition) of a template struct Example that receive a variadic list of type template parameters
template<typename... S>
struct Example;
You can also avoid to use the S and write simply
template <typename...>
struct Example;
because the name of the variadic list isn't used in this context.
At this point the compiler know that there is a variadic template struct Example but doesn't know how is made.
Next we add the definition of a specialization of Example that receive one or more template parameter (observe that Example is defined to receive zero or more parameter, so a specialization that receive one or more parameter is special case of Example)
//....... one --> V VVVVV <- or more template parameter
template<typename H, typename... T>
struct Example<H, T...>
{ // .........^^^^^^^^^ <- this is a specialization
static const size_t value = sizeof(H) + Example<T...>::value;
};
The <H, T...> part after Example identifies a specialization (as said).
This specialization define a static const size_t variable initialized with the sum of the sizeof(H) (the sizeof() of the first type template parameter) with the value defined in another Example class: Example<T...>.
So you're observing a recursive definition: value is the sum of the sizeof() of the first parameter (a type) with the sum of the sizeof() of the following types.
Suggestion: if you use variadic templates, you can use also constexpr, so better define value as constexpr
static constexpr std::size_t value = sizeof(H) + Example<T...>::value;
Or better, you can inherit from std::integral_constant
template <typename H, typename... T>
struct Example <H, T...>
: public std::integral_constant<std::size_t, sizeof(H) + Example<T...>{}>
{ };
so you inherit value from std::integral_constant with additional useful facilities (by example: automatic conversion to std::size_t in a context where a std::size_t is required)
Every recursion needs a ground case, so you have
template<>
struct Example<>
{
static const size_t value = 0;
};
the declaration of another specialization of Example; this time the case with exactly zero template parameter (Example<>). In this case you have the definition of a value that is zero to terminate the recursion.
As before, you can define value as constexpr or, better IMHO, using again std::integral_constant
template <>
struct Example<> : public std::integral_constant<std::size_t, 0u>
{ };
Now you have defined two specializations for Example: one for the one-or-more parameters cases, one for the zero-parameters case. So you have covered all cases for Example that is declared receiving zero-or-more parameters; there is no needs to declare the generic (not specialized version) of Example.
As observed by Deduplicator, you can define the generic case and only one specialization: if you write
template <typename...>
struct Example : public std::integral_constant<std::size_t, 0u>
{ };
template <typename T, typename ... Ts>
struct Example<T, Ts...>
: public std::integral_constant<std::size_t, sizeof(T)+Example<Ts...>{}>
{ };
you first declare Example receiving zero-or-more parameters and define the generic case with a value zero (the ground case), next you define a one-or-more specialization.
Considering that the compiler select the more specialized version (when more version matches), the compiler select the specialization when there is one-or-more parameters (bot versions match but the specialization is more specialized) and the generic version when there are zero parameters (because the specialization doesn't matches).
This way is a little more synthetic but can be less clear.
Could you please describe what will happen for the below call? which of the templates will be used and when?
Now should be simple to understand.
When you write
Example<long, int, char>::value
you ask for the value of Example<long, int, char>.
Three parameters, so the one-or-more specialization is selected, that is
value = sizeof(long) + Example<int, char>::value;
for the same reason, the value in Example<int, char> is
value = sizeof(int) + Example<char>::value;
and the value in Example<char> is
value = sizeof(char) + Example<>::value;
Now, for Example<>::value, the zero-parameters specialization is selected and Example<>::value is zero.
Concluding, we have that value in Example<long, int, char> is initialized with
value = sizeof(long) + sizeof(int) + sizeof(char) + 0;
You tagged C++11, so it's a pity you can't use C++17 (template folding) where you can avoid recursion at all and define Example as a using
template <typename ... Ts>
using Example = std::integral_constant<std::size_t, (... + sizeof(Ts))>;