数值模板参数与模板模板参数
 ● 模板可以接收(编译期常量)数值作为模板参数
 – template class Str;
template<int a>
int fun(int x)
{
    return x + a;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::cout << fun<3>(5) << std::endl;
    return a.exec();
}

– template <typename T, T value> class Str;
template<typename T, T a>
int fun(int x)
{
    return x + a;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::cout << fun<int, 3>(5) << std::endl;
    return a.exec();
}
– (C++ 17) template class Str;
template<auto a> //'auto' not allowed in template parameter until C++17
int fun()
{
    
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::cout << fun<3>() << std::endl;
    std::cout << fun<true>() << std::endl;
    return a.exec();
}
– (C++ 20) 接收字面值类对象与浮点数作为模板参数
template<float a> //A non-type template parameter cannot have type 'float' before C++20
int fun()
{
    
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    std::cout << fun<3.14f>() << std::endl;
    
    constexpr float x = 10000 - 10000 + 3.14;
    fun<x>();
    
    constexpr float x2 = 10000 - 3.14 + 10000;
    fun<x2>();
    return a.exec();
}
● 目前 clang 12 不支持接收浮点数作为模板参数
 ● 接收模板作为模板参数
 – template <template class C> class Str;
 – (C++17) template <template typename C> class Str;
template<typename T>
class C
{
    public:
     void show()
     {
        std::cout << "template<typename T> class C void show()\n";
     }
};
template<template<typename T> class T2>
//template<template<typename T> typename T2> //Since C++17
void fun()
{
    T2<int> tmp;
    std::cout << "template<template<typename T> class T2> void fun() T1<int> tmp\n";
    tmp.show();
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun<C>();
    return a.exec();
}

– C++17 开始,模板的模板实参考虑缺省模板实参( clang 12 支持程度有限)
template<typename T>
class C
{
    public:
     void show()
     {
        std::cout << "template<typename T> class C void show()\n";
     }
};
template<typename T, typename T2>
class C2
{
    public:
     void show()
     {
        std::cout << "template<typename T, typename T2> class C2 void show()\n";
     }
};
template<template<typename T, typename T2> class C3>
//template<template<typename T> typename T2> //Since C++17
void fun()
{
    C2<int, double> tmp;
    std::cout << "template<template<typename T, typename T2> class C3> void fun() C2<int, double> tmp\n";
    tmp.show();
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun<C2>();
    return a.exec();
}

template<typename T>
class C
{
    public:
     void show()
     {
        std::cout << "template<typename T> class C void show()\n";
     }
};
template<typename T, typename T2 = int>
class C2
{
    public:
     void show()
     {
        std::cout << "template<typename T, typename T2 = int> class C2 void show()\n";
     }
};
template<template<typename> typename T2>
void fun()
{
    C2<int, double> tmp;
    std::cout << "template<template<typename> typename T2> void fun() C2<int, double> tmp\n";
    tmp.show();
}
template<>
void fun<C2>() //OK
{
}
● Str 是否支持?
别名模板与变长模板
 ● 可以使用 using 引入别名模板
 – 为模板本身引入别名
template<typename T>
using MyType = T;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int val = 5;
    MyType<int> x = val;
    std::cout << x <<std::endl;
    return a.exec();
}

template<typename T>
using MyPointer = T*;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int val = 5;
    MyPointer<int> x = &val;
    std::cout << x << '\n' << *x <<std::endl;
    return a.exec();
}

– 为类模板的成员引入别名
template<typename T>
struct B
{
    using TP = T*;
};
template<typename T>
using MyPointer = typename B<T>::TP;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int val = 5;
    MyPointer<int> x = &val;
    std::cout << x << '\n' << *x <<std::endl;
    return a.exec();
}
– 别名模板不支持特化,但可以基于类模板的特化引入别名,以实现类似特化的功能
template<typename T>
using MyPointer = T*;
template<> //Explicit specialization of alias templates is not permitted
using MyPointer<int> = int&;
template<typename T>
struct B
{
    using type = T*;
};
template<>
struct B<int>
{
    using type = int&; //可以基于类模板的特化引入别名
};
template<typename T>
using MyPointer = typename B<T>::type;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    float val = 3.14;
    MyPointer<float> x = &val;
    std::cout << x << '\n' << *x << std::endl;
    return a.exec();
}

 ● 注意与实参推导的关系
template<typename T>
using M = T*;
template<typename T>
void fun(M<T> input) //等价于fun(T* input),于是可以进行模板实参推导
{
    std::cout << "template<typename T> void fun(M<T> input): " << *input;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int x = 2;
    fun(&x);
    return a.exec();
}

template<typename T>
struct B //如果入参是一般的类型比如int, float, double,则使用一般的类型作为此模板的T的值
{
    using type = T*;
};
template<typename T>
struct B<T*> //template<typename T> struct B的一个特化。如果入参是一般类型的指针比如int*, float*, double*,则使用一般类型的指针作为此模板的T的值
{
    using type = T*;
};
template<typename T>
using M = typename B<T>::type;
template<typename T>
void fun(M<T> input) //等价于fun(typename B<T>::type input),不能确定T的类型,fun(&x)不能进行模板实参推导
{
    std::cout << "template<typename T> void fun(M<T> input): " << *input;
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int x = 2;
    fun(&x); //Error: No matching function for call to 'fun', candidate template ignored: couldn't infer template argument 'T'
    return a.exec();
}
● 变长模板( Variadic Template )
 – 变长模板参数与参数包
template<int... a>
void fun()
{
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun<1, 2, 3>(); //OK
    return a.exec();
}
template<typename... a>
void fun()
{
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun<int, char, double>(); //OK
    return a.exec();
}
template<typename... T>
void fun(T... args)
{
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun<int, char, double>(3, 'c', 3.14); //OK
    return a.exec();
}
– 变长模板参数可以是数值、类型或模板
 – sizeof… 操作
template<typename... T>
void fun(T... args)
{
    std::cout << sizeof...(T) << std::endl; //输出等同于std::cout << sizeof...(args) << std::endl;,意为形参包里有多少个类型,而非底层的字节数
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun(3, 'c', 3.14);
    return a.exec();
}

– 注意变长模板参数的位置
template<typename... T>
class C;
template<typename T1, typename T2>
class B;
template<typename... T, typename T2>
class B<C<T...>, T2>
{
};
包展开与折叠表达式
 ● (C++11) 通过包展开技术操作变长模板参数
template<typename... T>
void fun(T... args)
{
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    fun<int, double, char>(3, 5.3, 'c'); //T... args展开成int, double, char
    return a.exec();
}
– 包展开语句可以很复杂,需要明确是哪一部分展开,在哪里展开
template<class... Us>
void f(Us... pargs) {}
 
template<class... Ts>
void g(Ts... args)
{
    f(&args...); // “&args...” is a pack expansion
                 // “&args” is its pattern
}
 
g(1, 0.2, "a"); // Ts... args expand to int E1, double E2, const char* E3
                // &args... expands to &E1, &E2, &E3
                // Us... pargs expand to int* E1, double* E2, const char** E3
template<typename...>
struct Tuple {};
 
template<typename T1, typename T2>
struct Pair {};
 
template<class... Args1>
struct zip
{
    template<class... Args2>
    struct with
    {
        typedef Tuple<Pair<Args1, Args2>...> type;
        // Pair<Args1, Args2>... is the pack expansion
        // Pair<Args1, Args2> is the pattern
    };
};
 
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// Pair<Args1, Args2>... expands to
// Pair<short, unsigned short>, Pair<int, unsigned int> 
// T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
 
// typedef zip<short>::with<unsigned short, unsigned>::type T2;
// error: pack expansion contains parameter packs of different lengths
● (C++17) 折叠表达式
void fun()
{
}
template<typename U, typename... T>
void fun(U u, T... args)
{
    std::cout << u << '\n';
    fun(args...);
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun(3, 5.3, 'C', "C++ Paramenter-Pack");
    return a.exec();
}

– 基于逗号的折叠表达式应用
template<typename... T>
void fun(T... args)
{
    ((std::cout << args << '\n'),...); //OK Since C++17
}
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    fun(3, 5.3, 'C', "C++ Paramenter-Pack");
    //((std::cout << args << '\n'),...);被处理成
    //std::cout << 3 << '\n', std::cout << 5.3 << '\n', std::cout << 'C' << '\n', std::cout << "C++ Paramenter-Pack" << '\n',逗号是右结合的
    //等价于
    //(std::cout << 3 << '\n', (std::cout << 5.3 << '\n', (std::cout << 'C' << '\n', (std::cout << "C++ Paramenter-Pack" << '\n'))))
    return a.exec();
}
– 折叠表达式用于表达式求值,无法处理输入(输出)是类型与模板的情形
参考
 深蓝学院:C++基础与深度解析
 cppreference : parameter_pack
 cppreference : fold



















