另外,为什么第一个模板+类声明在结构声明之后缺少“”?(请参阅注释掉的内容)?什么时候添加合适,什么时候不合适?
在我看来,最好从这一点开始。
首先,以下(删除<S...> 注释)是模板结构Example 的声明(注意:仅声明,不是定义),它接收类型的可变参数列表模板参数
template<typename... S>
struct Example;
你也可以避免使用S,简单地写
template <typename...>
struct Example;
因为在此上下文中未使用可变参数列表的名称。
此时编译器知道有一个可变参数模板结构Example,但不知道是如何制作的。
接下来我们添加接收一个或多个模板参数的Example的specialization的定义(注意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())和另一个Example类中定义的value之和初始化:Example<T...>。
所以您观察到递归定义:value 是第一个参数(一种类型)的 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 零定义通用案例(基本案例),然后定义一个或多个特化。
考虑到编译器选择更专业化的版本(当更多版本匹配时),编译器在有一个或多个参数时选择专业化(机器人版本匹配但专业化更专业化)和通用版本是零参数(因为专业化不匹配)。
这种方式比较综合,但可能不太清楚。
您能否描述一下下面的电话会发生什么?将使用哪些模板以及何时使用?
现在应该很容易理解了。
当你写作时
Example<long, int, char>::value
您要求value 的Example<long, int, char>。
三个参数,所以选择一个或多个特化,即
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,选择零参数特化,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))>;