> 文章列表 > C++模板基础(八)

C++模板基础(八)

C++模板基础(八)

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

C++模板基础(八)

– 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++模板基础(八)

– 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();
}

C++模板基础(八)

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();
}

C++模板基础(八)

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();
}

C++模板基础(八)

– 为类模板的成员引入别名

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();
}

C++模板基础(八)
● 注意与实参推导的关系

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();
}

C++模板基础(八)

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>(); //OKreturn a.exec();
}
template<typename... a>
void fun()
{
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);fun<int, char, double>(); //OKreturn 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); //OKreturn 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();
}

C++模板基础(八)

– 注意变长模板参数的位置

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, charreturn 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();
}

C++模板基础(八)

– 基于逗号的折叠表达式应用

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