C++模板元编程学习笔记 1: 模板的模板参数

由Jeza Chen 发表于 July 16, 2025

模板的模板参数是C++模板元编程一个重要的基础概念。它允许我们在模板中使用其他模板作为参数,从而实现更复杂的类型和行为。

首先看一段简单的代码示例:

#include <iostream>
#include <tuple>

using namespace std;

template<typename>
struct first_type_getter {};

template<template<typename...> typename Ts, typename T1, typename... TN>
struct first_type_getter<Ts<T1, TN...>> {
    using type = T1;
};

// 不能这样子写
// 这样的话,first_type需要提供不止一个模板参数的
// 原本的 first_type<tuple<int, float, double>> 就要变成 first_type<tuple, int, float, double>
// template<template<typename...> typename Ts, typename T1, typename... TN>
// using first_type = typename first_type_getter<Ts<T1, TN...>>::type;

template<typename T>
using first_type = typename first_type_getter<T>::type;

int main(){
    cout << is_same_v<first_type<tuple<int, float, double>>, int> << endl;  // 1
    cout << is_same_v<first_type<tuple<int, float, double>>, float> << endl;  // 0
    cout << is_same_v<first_type<tuple<int, float, double>>, long> << endl; // 0
}

在这里,我们首先定义了一个通用的模板 first_type_getter,它接受一个类型参数。然后,我们对这个模板进行了偏特化,通过模板的模板参数使其能够深入获取参数内部的类型。这个偏特化版本提取了模板参数Ts中的第一个类型 T1,并将其定义为 type。在后续使用时,我们就可以通过first_type<std::tuple<int, float, double>>来获取元组的第一个类型int

模板的模板参数语法

需要注意的是,使用模板的模板参数时,正确的语法应该长这样 template<template<typename...> typename Ts>。其中template<typename...>表示这个参数是一个模板,然后后面还需要加上typename以表明这是一个类型参数。

在上述的例子中,即便Ts的实参是一个类型别名,编译器仍然能够正确解析。

比如,如果我们使用using MyTuple = std::tuple<int, float, double>;,然后调用 first_type<MyTuple>,编译器仍然能够正确解析出 int 作为第一个类型。

为什么这里需要以偏特化的方式来实现第一个类型获取?

我们先假定不用偏特化的形式,直接定义一个模板:

template<template<typename...> typename Ts, typename T1, typename... TN>
struct first_type_getter {
    using type = T1;
};

如此,我们可以使用first_type_getter<std::tuple<int, float, double>>来获取第一个类型吗?答案是否定的。因为在这种情况下,first_type_getter的实例化需要提供不止一个模板参数(因为形参列表有多个参数),和我们的本意相悖。由于我们只需要传一个模板实参,且目的是从该模板实参中提取出内部的参数,所以只能先定义一个通用的、接受一个类型参数的模板,然后再对其进行偏特化。