数值模板参数与模板模板参数
● 模板可以接收(编译期常量)数值作为模板参数
– 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