了解模板的声明、定义和特化
Understanding the declaration, definition and specialization of templates
我试图理解下面的示例,但我对三种不同的模板和结构声明感到有点困惑。
你能描述一下下面的调用会发生什么吗?将使用哪些模板以及何时使用?
另外,为什么第一个模板+class 声明在结构声明之后缺少“<S...>
”?(看看注释掉的内容)?什么时候适合,什么时候不适合?
#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;
}
输出:13
首先声明一个名为Example
的struct
模板,接受任意数量的类型:
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;
};
请注意 template
后跟空 <>
-括号。
在完全特化之前定义部分特化并不重要,因为必须推迟实例化,直到知道模板类型参数。
您使用的特定实例 Example<long,int,char>::value
取决于 Example<int, char>::value
,而后者又取决于 Example<char>
,这导致基本情况:
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;
当然,例子可以简化:
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;
};
或使用 C++17 折叠表达式:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
顺便说一句,从不使用 using namespace std;
是有充分理由的,我想知道为什么 #include <stdio.h>
,而 return 0;
对于 main()
.[=28 是多余的=]
只回答这部分问题:
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?
当您对模板化 function/class/struct/type 进行 (一般)声明 时,您只使用一次尖括号< >
,声明前:
template <typename T>
void foo(T x);
当你声明通用模板的特定实例化时,你使用< >
两次,一次在声明前为空,然后再次使用您要为其实例化的特定模板参数:
template <>
void foo<int>(int& x);
当您声明通用模板的特定 专业化 时,您使用 < >
一次,使用您要使用的特定模板参数实例化:
template
void foo<int>(int& x);
关于最后两项的更多信息(以及它们的区别):
Difference between instantiation and specialization in c++ templates
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?
我觉得还是从这里开始比较好
首先,下面(去掉了<S...>
的注释)是一个template struct [=25的声明(注意:只是声明,不是定义) =] 接收类型模板参数的可变列表
template<typename... S>
struct Example;
你也可以避免使用S
而简单地写
template <typename...>
struct Example;
因为可变参数列表的名称未在此上下文中使用。
此时编译器知道有一个可变参数模板结构 Example
但不知道它是如何制作的。
接下来我们添加定义 specialization of Example
接收一个或多个模板参数(注意Example
被定义为接收零个或多个参数,因此接收一个或多个参数的特化是 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;
};
Example
之后的 <H, T...>
部分标识了 专业化 (如前所述)。
此特化定义了一个 static const size_t
变量,该变量初始化为 sizeof(H)
(第一个类型模板参数的 sizeof()
)与另一个中定义的 value
之和Example
class: Example<T...>
.
所以你正在观察递归定义:值是第一个参数(类型)的 sizeof()
与以下类型的 sizeof()
的总和。
建议:如果你使用可变参数模板,你也可以使用constexpr
,所以最好将value
定义为constexpr
static constexpr std::size_t value = sizeof(H) + Example<T...>::value;
或者更好的是,您可以继承自 std::integral_constant
template <typename H, typename... T>
struct Example <H, T...>
: public std::integral_constant<std::size_t, sizeof(H) + Example<T...>{}>
{ };
因此您从 std::integral_constant
继承 value
并具有额外的有用功能(例如:在需要 std::size_t
的上下文中自动转换为 std::size_t
)
每个递归都需要一个基础案例,所以你有
template<>
struct Example<>
{
static const size_t value = 0;
};
声明 Example
的另一个专业化;这次模板参数正好为零 (Example<>
)。在这种情况下,您有一个 value
的定义,它是零以终止递归。
和以前一样,您可以将 value
定义为 constexpr
或者,更好的恕我直言,再次使用 std::integral_constant
template <>
struct Example<> : public std::integral_constant<std::size_t, 0u>
{ };
现在您已经为 Example
定义了两个特化:一个用于一个或多个参数的情况,一个用于零参数的情况。因此,您已经涵盖了 Example
声明接收零个或多个参数的所有情况;无需声明 Example
.
的通用(非专用版本)
正如 Deduplicator 所观察到的,您可以 定义 通用案例并且只有一个专业化:如果您编写
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...>{}>
{ };
您首先声明 Example
接收零个或多个参数并定义具有 value
零的通用情况(基本情况),接下来您定义一个或多个专业化。
考虑到编译器select更专业的版本(当更多版本匹配时),当有一个或多个参数时编译器select专业化(机器人版本匹配但专业化更专业化)和参数为零的通用版本(因为专业化不匹配)。
这种方式更综合一点,但不太清晰。
Could you please describe what will happen for the below call? which of the templates will be used and when?
现在应该很容易理解了。
写的时候
Example<long, int, char>::value
您要求 Example<long, int, char>
的 value
。
三个参数,所以一个或多个特化是selected,即
value = sizeof(long) + Example<int, char>::value;
同理,Example<int, char>
中的value
是
value = sizeof(int) + Example<char>::value;
而 Example<char>
中的 value
是
value = sizeof(char) + Example<>::value;
现在,对于 Example<>::value
,零参数特化是 selected 并且 Example<>::value
是零。
最后,Example<long, int, char>
中的 value
被初始化为
value = sizeof(long) + sizeof(int) + sizeof(char) + 0;
你标记了 C++11,所以很遗憾你不能使用 C++17(模板折叠),在那里你可以完全避免递归并将 Example
定义为 using
template <typename ... Ts>
using Example = std::integral_constant<std::size_t, (... + sizeof(Ts))>;
我试图理解下面的示例,但我对三种不同的模板和结构声明感到有点困惑。
你能描述一下下面的调用会发生什么吗?将使用哪些模板以及何时使用?
另外,为什么第一个模板+class 声明在结构声明之后缺少“<S...>
”?(看看注释掉的内容)?什么时候适合,什么时候不适合?
#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;
}
输出:13
首先声明一个名为Example
的struct
模板,接受任意数量的类型:
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;
};
请注意 template
后跟空 <>
-括号。
在完全特化之前定义部分特化并不重要,因为必须推迟实例化,直到知道模板类型参数。
您使用的特定实例 Example<long,int,char>::value
取决于 Example<int, char>::value
,而后者又取决于 Example<char>
,这导致基本情况:
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;
当然,例子可以简化:
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;
};
或使用 C++17 折叠表达式:
template <class... T>
struct Example {
static const size_t value = 0 + ... + sizeof(T);
};
顺便说一句,从不使用 using namespace std;
是有充分理由的,我想知道为什么 #include <stdio.h>
,而 return 0;
对于 main()
.[=28 是多余的=]
只回答这部分问题:
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?
当您对模板化 function/class/struct/type 进行 (一般)声明 时,您只使用一次尖括号
< >
,声明前:template <typename T> void foo(T x);
当你声明通用模板的特定实例化时,你使用
< >
两次,一次在声明前为空,然后再次使用您要为其实例化的特定模板参数:template <> void foo<int>(int& x);
当您声明通用模板的特定 专业化 时,您使用
< >
一次,使用您要使用的特定模板参数实例化:template void foo<int>(int& x);
关于最后两项的更多信息(以及它们的区别):
Difference between instantiation and specialization in c++ templates
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?
我觉得还是从这里开始比较好
首先,下面(去掉了<S...>
的注释)是一个template struct [=25的声明(注意:只是声明,不是定义) =] 接收类型模板参数的可变列表
template<typename... S>
struct Example;
你也可以避免使用S
而简单地写
template <typename...>
struct Example;
因为可变参数列表的名称未在此上下文中使用。
此时编译器知道有一个可变参数模板结构 Example
但不知道它是如何制作的。
接下来我们添加定义 specialization of Example
接收一个或多个模板参数(注意Example
被定义为接收零个或多个参数,因此接收一个或多个参数的特化是 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;
};
Example
之后的 <H, T...>
部分标识了 专业化 (如前所述)。
此特化定义了一个 static const size_t
变量,该变量初始化为 sizeof(H)
(第一个类型模板参数的 sizeof()
)与另一个中定义的 value
之和Example
class: Example<T...>
.
所以你正在观察递归定义:值是第一个参数(类型)的 sizeof()
与以下类型的 sizeof()
的总和。
建议:如果你使用可变参数模板,你也可以使用constexpr
,所以最好将value
定义为constexpr
static constexpr std::size_t value = sizeof(H) + Example<T...>::value;
或者更好的是,您可以继承自 std::integral_constant
template <typename H, typename... T>
struct Example <H, T...>
: public std::integral_constant<std::size_t, sizeof(H) + Example<T...>{}>
{ };
因此您从 std::integral_constant
继承 value
并具有额外的有用功能(例如:在需要 std::size_t
的上下文中自动转换为 std::size_t
)
每个递归都需要一个基础案例,所以你有
template<>
struct Example<>
{
static const size_t value = 0;
};
声明 Example
的另一个专业化;这次模板参数正好为零 (Example<>
)。在这种情况下,您有一个 value
的定义,它是零以终止递归。
和以前一样,您可以将 value
定义为 constexpr
或者,更好的恕我直言,再次使用 std::integral_constant
template <>
struct Example<> : public std::integral_constant<std::size_t, 0u>
{ };
现在您已经为 Example
定义了两个特化:一个用于一个或多个参数的情况,一个用于零参数的情况。因此,您已经涵盖了 Example
声明接收零个或多个参数的所有情况;无需声明 Example
.
正如 Deduplicator 所观察到的,您可以 定义 通用案例并且只有一个专业化:如果您编写
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...>{}>
{ };
您首先声明 Example
接收零个或多个参数并定义具有 value
零的通用情况(基本情况),接下来您定义一个或多个专业化。
考虑到编译器select更专业的版本(当更多版本匹配时),当有一个或多个参数时编译器select专业化(机器人版本匹配但专业化更专业化)和参数为零的通用版本(因为专业化不匹配)。
这种方式更综合一点,但不太清晰。
Could you please describe what will happen for the below call? which of the templates will be used and when?
现在应该很容易理解了。
写的时候
Example<long, int, char>::value
您要求 Example<long, int, char>
的 value
。
三个参数,所以一个或多个特化是selected,即
value = sizeof(long) + Example<int, char>::value;
同理,Example<int, char>
中的value
是
value = sizeof(int) + Example<char>::value;
而 Example<char>
中的 value
是
value = sizeof(char) + Example<>::value;
现在,对于 Example<>::value
,零参数特化是 selected 并且 Example<>::value
是零。
最后,Example<long, int, char>
中的 value
被初始化为
value = sizeof(long) + sizeof(int) + sizeof(char) + 0;
你标记了 C++11,所以很遗憾你不能使用 C++17(模板折叠),在那里你可以完全避免递归并将 Example
定义为 using
template <typename ... Ts>
using Example = std::integral_constant<std::size_t, (... + sizeof(Ts))>;