
从 C++17 开始,我们可以使用 auto 和 decltype(auto) 作为非类型模板参数的类型,这大大增强了模板的泛型编程能力。
基础用法:auto 作为非类型模板参数
#include<iostream>
#include<type_traits>
// 使用 auto 作为非类型模板参数
template<auto N>
classValueWrapper {
public:
voidprint()const{
std::cout << "Value: " << N
<< ", Type: " << typeid(N).name()
<< std::endl;
}
autogetValue()const{ return N; }
};
// 部分特化:针对指针类型
template<auto* Ptr>
classPointerWrapper {
public:
voidprint()const{
std::cout << "Pointer address: " << Ptr << std::endl;
ifconstexpr(!std::is_null_pointer_v<decltype(Ptr)>){
std::cout << "Pointed value: " << *Ptr << std::endl;
}
}
};
intmain(){
// 不同类型实例化
ValueWrapper<42> v1; // int 类型
ValueWrapper<'A'> v2; // char 类型
ValueWrapper<true> v3; // bool 类型
ValueWrapper<3.14f> v4; // float 类型
ValueWrapper<&v1> v5; // 指针类型
v1.print(); // Value: 42, Type: int
v2.print(); // Value: A, Type: char
v3.print(); // Value: 1, Type: bool
v4.print(); // Value: 3.14, Type: float
// 注意:double 和字符串字面量不能作为非类型模板参数
// ValueWrapper<3.14> v6; // 错误:double 不是允许的模板参数类型
// ValueWrapper<"hello"> v7; // 错误:字符串字面量不能作为模板参数
return0;
}
结合类型推导和部分特化
#include<iostream>
#include<array>
// 主模板:通用处理
template<auto N>
structFixedArray {
staticconstexprauto size = N;
using value_type = decltype(N);
voidinfo()const{
std::cout << "通用版本 - 大小: " << size
<< ", 类型: " << typeid(value_type).name() << std::endl;
}
};
// 部分特化:针对 int 类型
template<int N>
structFixedArray<N> {
staticconstexprauto size = N;
voidinfo()const{
std::cout << "int 特化版本 - 大小: " << size << std::endl;
}
std::array<int, N> getArray()const{
std::array<int, N> arr{};
for (int i = 0; i < N; ++i) arr[i] = i;
return arr;
}
};
// 部分特化:针对枚举类型
enumclassColor { Red, Green, Blue };
template<Color C>
structFixedArray<C> {
voidinfo()const{
std::cout << "枚举特化版本 - 颜色: ";
switch(C) {
case Color::Red: std::cout << "红"; break;
case Color::Green: std::cout << "绿"; break;
case Color::Blue: std::cout << "蓝"; break;
}
std::cout << std::endl;
}
};
intmain(){
FixedArray<42> fa1; // 匹配 int 特化版本
FixedArray<'X'> fa2; // 使用通用版本(char)
FixedArray<100ull> fa3; // 使用通用版本(unsigned long long)
FixedArray<Color::Green> fa4; // 匹配枚举特化版本
fa1.info(); // int 特化版本 - 大小: 42
fa2.info(); // 通用版本 - 大小: X, 类型: char
fa3.info(); // 通用版本 - 大小: 100, 类型: y (unsigned long long)
fa4.info(); // 枚举特化版本 - 颜色: 绿
auto arr = fa1.getArray();
std::cout << "数组元素: " << arr[0] << ", " << arr[5] << std::endl;
return0;
}
高级用法:decltype(auto) 和类模板参数推导
#include<iostream>
#include<vector>
#include<array>
// 使用 decltype(auto) 保持精确类型
template<decltype(auto) N>
structPreciseType {
voidprint()const{
std::cout << "值: " << N
<< ", 精确类型: " << typeid(decltype(N)).name()
<< std::endl;
}
};
// 结合类模板参数推导
template<typename T, auto N>
classMatrix {
private:
std::array<T, N * N> data;
public:
// 构造函数模板
template<typename... Args>
Matrix(Args&&... args) : data{std::forward<Args>(args)...} {}
voidprint()const{
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < N; ++j) {
std::cout << data[i * N + j] << " ";
}
std::cout << std::endl;
}
}
autogetSize()const{ return N; }
};
// C++17 类模板参数推导指引
template<typename T, auto N>
Matrix(T(&)[N * N]) -> Matrix<T, N>;
intmain(){
// decltype(auto) 示例
int x = 42;
int& ref = x;
PreciseType<42> pt1; // 值类型
PreciseType<(ref)> pt2; // 引用类型(注意括号)
pt1.print(); // 值: 42, 精确类型: int
pt2.print(); // 值: 42, 精确类型: int
// 类模板参数推导示例
int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 3x3 矩阵
Matrix m1(arr1); // 推导出 Matrix<int, 3>
m1.print();
std::cout << "矩阵大小: " << m1.getSize() << "x" << m1.getSize() << std::endl;
// 直接指定类型
Matrix<double, 2> m2{1.0, 2.0, 3.0, 4.0};
m2.print();
return0;
}
实际应用:编译期计算和类型转换
#include<iostream>
#include<tuple>
// 编译期整数序列生成
template<auto Start, auto End, auto Step = 1>
struct IntegerRange {
staticconstexprauto size = (End - Start + Step - 1) / Step;
template<typename F>
staticconstexprvoidforEach(F&& func){
ifconstexpr(Start < End){
func(Start);
IntegerRange<Start + Step, End, Step>::forEach(std::forward<F>(func));
}
}
staticconstexprautogetArray(){
std::array<int, size> arr{};
for (size_t i = 0; i < size; ++i) {
arr[i] = Start + i * Step;
}
return arr;
}
};
// 编译期单位转换
template<auto Value>
structUnit {
staticconstexprauto value = Value;
template<typename TargetUnit>
staticconstexprautoconvert(){
ifconstexpr(std::is_same_v<decltype(Value), constint&>){
return Value; // 保持原值
} else {
return Value;
}
}
};
// 使用 decltype(auto) 保存 constexpr lambda
template<auto Func>
structFunctionWrapper {
staticconstexprautoapply(auto&&... args){
return Func(std::forward<decltype(args)>(args)...);
}
};
intmain(){
// 整数序列示例
using Range = IntegerRange<0, 10, 2>;
std::cout << "序列大小: " << Range::size << std::endl;
std::cout << "序列元素: ";
Range::forEach([](auto elem) {
std::cout << elem << " ";
});
std::cout << std::endl;
auto arr = Range::getArray();
std::cout << "数组: ";
for (auto v : arr) std::cout << v << " ";
std::cout << std::endl;
// 单位转换示例
Unit<100> u1;
Unit<3.14f> u2;
std::cout << "u1: " << u1.value << std::endl;
std::cout << "u2: " << u2.value << std::endl;
// C++20 后支持 constexpr lambda 作为模板参数
constexprauto square = [](auto x) { return x * x; };
FunctionWrapper<square> wrapper;
std::cout << "平方: " << wrapper.apply(5) << std::endl;
return0;
}
重要限制
允许的类型:整数类型、枚举类型、指针、引用、 std::nullptr_t不允许的类型:浮点数(除 float外)、字符串字面量、自定义类型(C++20 前)
// 允许的示例
template<auto N> classC1 {}; // 各种整数类型
template<auto* P> classC2 {}; // 指针
template<auto& R> classC3 {}; // 引用
template<auto E> classC4 {}; // 枚举
// 不允许的示例(C++17 中)
// template<auto D> class C5 {}; // double 不允许(float 允许)
// template<auto S> class C6 {}; // 字符串字面量不允许
// template<auto S> class C7 {}; // std::string 不允许
总结
auto 和 decltype(auto) 作为模板参数极大地提高了代码的泛用性,特别是在:
编译期计算和元编程 泛型容器和算法 类型安全的常量传递 与 C++17 类模板参数推导的协同工作
这使得我们可以编写更简洁、更通用的模板代码,同时保持类型安全。
夜雨聆风