Declaring and defining template, and specialising them
I'm trying to understand the below example, but I'm a bit confused from the three different template and struct declarations.
Could you please describe what will happen for the below call? which of the templates will be used and when?
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?
#include <iostream>
#include <stdio.h>
using namespace std;
template<typename... S>
struct Example /* <S...> */ ;
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
template<>
struct Example<>
{
static const size_t value = 0;
};
int main(){
cout << Example<long, int, char>::value << endl;
return 0;
}
Output: 13
c++ c++11 templates variadic-templates template-specialization
New contributor
add a comment |
I'm trying to understand the below example, but I'm a bit confused from the three different template and struct declarations.
Could you please describe what will happen for the below call? which of the templates will be used and when?
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?
#include <iostream>
#include <stdio.h>
using namespace std;
template<typename... S>
struct Example /* <S...> */ ;
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
template<>
struct Example<>
{
static const size_t value = 0;
};
int main(){
cout << Example<long, int, char>::value << endl;
return 0;
}
Output: 13
c++ c++11 templates variadic-templates template-specialization
New contributor
add a comment |
I'm trying to understand the below example, but I'm a bit confused from the three different template and struct declarations.
Could you please describe what will happen for the below call? which of the templates will be used and when?
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?
#include <iostream>
#include <stdio.h>
using namespace std;
template<typename... S>
struct Example /* <S...> */ ;
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
template<>
struct Example<>
{
static const size_t value = 0;
};
int main(){
cout << Example<long, int, char>::value << endl;
return 0;
}
Output: 13
c++ c++11 templates variadic-templates template-specialization
New contributor
I'm trying to understand the below example, but I'm a bit confused from the three different template and struct declarations.
Could you please describe what will happen for the below call? which of the templates will be used and when?
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?
#include <iostream>
#include <stdio.h>
using namespace std;
template<typename... S>
struct Example /* <S...> */ ;
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
template<>
struct Example<>
{
static const size_t value = 0;
};
int main(){
cout << Example<long, int, char>::value << endl;
return 0;
}
Output: 13
c++ c++11 templates variadic-templates template-specialization
c++ c++11 templates variadic-templates template-specialization
New contributor
New contributor
edited 2 hours ago
max66
37.7k74370
37.7k74370
New contributor
asked 3 hours ago
katkatokatkato
362
362
New contributor
New contributor
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
The first declares the template of a struct
named Example
, accepting any number of types:
template<typename... S>
struct Example /* <S...> */ ;
If the name of the newly declared template was followed by <>
, with or without arguments, it would be a specialization instead!
The second defines a partial specialization for at least one type-argument:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
And the last one defines a full specialization for no type-arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Take note that template
is followed by empty <>
-brackets.
It doesn't matter that the partial specialization is defined before the full specialization because instantiation must be deferred until the templates type-arguments are known.
The specific instance you use, Example<long,int,char>::value
, depends on Example<int, char>::value
, which depends on Example<char>
, which leads to the base-case:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Of course, the example could be simplified:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Or with C++17 fold-expressions:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
As an aside, there are good reasons never to use using namespace std;
, I wonder why you #include <stdio.h>
, and return 0;
is redundant for main()
.
add a comment |
Only answering this part of your question:
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?
When you declare a templated function/class/struct/type, you only use the angle bracket
< >
once, before the declaration:
template <typename T>
void foo(T x);
When you declare a specific instantiation of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template <>
void foo<int>(int& x);
When you declare a specific specialization of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template
void foo<int>(int& x);
More on the last two items (and how they differ):
Difference between instantiation and specialization in c++ templates
add a comment |
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))>;
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
katkato is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55109599%2fdeclaring-and-defining-template-and-specialising-them%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The first declares the template of a struct
named Example
, accepting any number of types:
template<typename... S>
struct Example /* <S...> */ ;
If the name of the newly declared template was followed by <>
, with or without arguments, it would be a specialization instead!
The second defines a partial specialization for at least one type-argument:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
And the last one defines a full specialization for no type-arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Take note that template
is followed by empty <>
-brackets.
It doesn't matter that the partial specialization is defined before the full specialization because instantiation must be deferred until the templates type-arguments are known.
The specific instance you use, Example<long,int,char>::value
, depends on Example<int, char>::value
, which depends on Example<char>
, which leads to the base-case:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Of course, the example could be simplified:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Or with C++17 fold-expressions:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
As an aside, there are good reasons never to use using namespace std;
, I wonder why you #include <stdio.h>
, and return 0;
is redundant for main()
.
add a comment |
The first declares the template of a struct
named Example
, accepting any number of types:
template<typename... S>
struct Example /* <S...> */ ;
If the name of the newly declared template was followed by <>
, with or without arguments, it would be a specialization instead!
The second defines a partial specialization for at least one type-argument:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
And the last one defines a full specialization for no type-arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Take note that template
is followed by empty <>
-brackets.
It doesn't matter that the partial specialization is defined before the full specialization because instantiation must be deferred until the templates type-arguments are known.
The specific instance you use, Example<long,int,char>::value
, depends on Example<int, char>::value
, which depends on Example<char>
, which leads to the base-case:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Of course, the example could be simplified:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Or with C++17 fold-expressions:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
As an aside, there are good reasons never to use using namespace std;
, I wonder why you #include <stdio.h>
, and return 0;
is redundant for main()
.
add a comment |
The first declares the template of a struct
named Example
, accepting any number of types:
template<typename... S>
struct Example /* <S...> */ ;
If the name of the newly declared template was followed by <>
, with or without arguments, it would be a specialization instead!
The second defines a partial specialization for at least one type-argument:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
And the last one defines a full specialization for no type-arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Take note that template
is followed by empty <>
-brackets.
It doesn't matter that the partial specialization is defined before the full specialization because instantiation must be deferred until the templates type-arguments are known.
The specific instance you use, Example<long,int,char>::value
, depends on Example<int, char>::value
, which depends on Example<char>
, which leads to the base-case:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Of course, the example could be simplified:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Or with C++17 fold-expressions:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
As an aside, there are good reasons never to use using namespace std;
, I wonder why you #include <stdio.h>
, and return 0;
is redundant for main()
.
The first declares the template of a struct
named Example
, accepting any number of types:
template<typename... S>
struct Example /* <S...> */ ;
If the name of the newly declared template was followed by <>
, with or without arguments, it would be a specialization instead!
The second defines a partial specialization for at least one type-argument:
template<typename H, typename... T>
struct Example<H, T...>
{
static const size_t value = sizeof(H) + Example<T...>::value;
};
And the last one defines a full specialization for no type-arguments:
template<>
struct Example<>
{
static const size_t value = 0;
};
Take note that template
is followed by empty <>
-brackets.
It doesn't matter that the partial specialization is defined before the full specialization because instantiation must be deferred until the templates type-arguments are known.
The specific instance you use, Example<long,int,char>::value
, depends on Example<int, char>::value
, which depends on Example<char>
, which leads to the base-case:
Example<long, int, char>::value = sizeof(long) + Example<int, char>::value; // sizeof(long) + sizeof(int) + 1 + 0
Example<int, char>::value = sizeof(int) + Example<char>::value; // sizeof(int) + 1 + 0
Example<char>::value = sizeof(char) + Example<>::value; // 1 + 0
Example<>::value = 0;
Of course, the example could be simplified:
template <class... T>
struct Example {
static const size_t value = 0;
static_assert(!sizeof...(T), "The base-template only handles no template arguments.");
};
template <class H, class... T>
struct Example {
static const size_t value = sizeof(H) + Example<T...>::example;
};
Or with C++17 fold-expressions:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
As an aside, there are good reasons never to use using namespace std;
, I wonder why you #include <stdio.h>
, and return 0;
is redundant for main()
.
edited 1 hour ago
alter igel
2,76811027
2,76811027
answered 2 hours ago
DeduplicatorDeduplicator
34.7k64990
34.7k64990
add a comment |
add a comment |
Only answering this part of your question:
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?
When you declare a templated function/class/struct/type, you only use the angle bracket
< >
once, before the declaration:
template <typename T>
void foo(T x);
When you declare a specific instantiation of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template <>
void foo<int>(int& x);
When you declare a specific specialization of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template
void foo<int>(int& x);
More on the last two items (and how they differ):
Difference between instantiation and specialization in c++ templates
add a comment |
Only answering this part of your question:
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?
When you declare a templated function/class/struct/type, you only use the angle bracket
< >
once, before the declaration:
template <typename T>
void foo(T x);
When you declare a specific instantiation of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template <>
void foo<int>(int& x);
When you declare a specific specialization of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template
void foo<int>(int& x);
More on the last two items (and how they differ):
Difference between instantiation and specialization in c++ templates
add a comment |
Only answering this part of your question:
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?
When you declare a templated function/class/struct/type, you only use the angle bracket
< >
once, before the declaration:
template <typename T>
void foo(T x);
When you declare a specific instantiation of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template <>
void foo<int>(int& x);
When you declare a specific specialization of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template
void foo<int>(int& x);
More on the last two items (and how they differ):
Difference between instantiation and specialization in c++ templates
Only answering this part of your question:
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?
When you declare a templated function/class/struct/type, you only use the angle bracket
< >
once, before the declaration:
template <typename T>
void foo(T x);
When you declare a specific instantiation of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template <>
void foo<int>(int& x);
When you declare a specific specialization of the general template, you use
< >
twice, once empty before the declaration, then again with the specific template parameters for which you're instantating:
template
void foo<int>(int& x);
More on the last two items (and how they differ):
Difference between instantiation and specialization in c++ templates
edited 2 hours ago
answered 2 hours ago
einpoklumeinpoklum
35.4k27129254
35.4k27129254
add a comment |
add a comment |
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))>;
add a comment |
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))>;
add a comment |
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))>;
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))>;
edited 1 hour ago
answered 2 hours ago
max66max66
37.7k74370
37.7k74370
add a comment |
add a comment |
katkato is a new contributor. Be nice, and check out our Code of Conduct.
katkato is a new contributor. Be nice, and check out our Code of Conduct.
katkato is a new contributor. Be nice, and check out our Code of Conduct.
katkato is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55109599%2fdeclaring-and-defining-template-and-specialising-them%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown