第 3 章 非类型模板参数

类模板的非类型参数

简单来说,模板参数,并不一定是类型参数(如 typename T),也可以是非类型参数(如int等)

比如,可以设计方便用户自定义容量的stack:

#include <array>
#include <cassert>
#include <iostream>

// Maxsize 指定了Stack的最大容量
template<typename T, std::size_t MaxSize>
class Stack {
private:
    std::array<T,MaxSize> elems; // 固定容量的数据,来限制栈的大小
    std::size_t cur_size{0}; //当前栈的大小, 直接初始化为0
public:
    Stack& operator=(const Stack&);
    void push(const T& elem);
    void pop(); 
    const T& top() const;
    bool empty() const {
        return cur_size == 0;
    }
};

template<typename T, std::size_t MaxSize>
void Stack<T,MaxSize>::push(const T& elem) {
    assert(cur_size < MaxSize);
    elems[cur_size++] = elem;
}

template<typename T, std::size_t MaxSize>
void Stack<T, MaxSize>::pop() {
    assert(!empty());
    cur_size--;
}

template<typename T, std::size_t MaxSize>
const T& Stack<T, MaxSize>::top() const {
    assert(!empty());
    return elems[cur_size-1];
}

int main() {
    Stack<int, 20> int20stack; // 申明一个容量大小为20, 元素类型为int的stack
    Stack<int, 40> int40stack; // 申明一个容量大小为40, 元素类型为int的stack
    Stack<std::string,40> stringStack; // 申明一个容量大小为40, 元素类型为string的stack

    int20stack.push(7); 
    std::cout << int20stack.top() << '\n';
    int20stack.pop();

    stringStack.push("hello");
    std::cout << stringStack.top() << '\n'; 
    stringStack.pop();

    return 0;
}

函数模板的非类型参数

同样,函数模板也可以使用非类型参数

比如,想要给vector中的每个元素都加上一个固定值,则可以:

#include <vector>
#include <algorithm>
#include <iostream>

template<int Val, typename T>
T addValue(T x) {
    return x + Val;
}

int main() {
    std::vector<int> arr{1, 2, 3, 4, 5};

    // 遍历容器内的每个元素,然后全加2
    std::transform(arr.begin(), arr.end(), arr.begin(), addValue<2,int>); 
    for(auto it : arr) {
        std::cout << it << std::endl;
    }
}

或者使用C++的auto 模板参数推导:

// C++ 17 新特性, 可以使用auto 来描述模板参数
template<auto Val, typename T = decltype(Val)>
T addValue(T x) {
    return x + Val;
}


int main() {
    std::vector<int> arr{1, 2, 3, 4, 5};

    // 遍历容器内的每个元素,然后全加2
    std::transform(arr.begin(), arr.end(), arr.begin(), addValue<3>); 
    for(auto it : arr) {
        std::cout << it << std::endl;
    }
}
// 或者确保传递的值,与传递的类型相同
template<typename T, T Val = T{}>
T addValue(T x) {
    return x + Val;
}


int main() {
    std::vector<int> arr{1, 2, 3, 4, 5};

    // 遍历容器内的每个元素,然后全加2
    std::transform(arr.begin(), arr.end(), arr.begin(), addValue<int,6>); 
    for(auto it : arr) {
        std::cout << it << std::endl;
    }
}

非模板参数限制

  1. 非类型模板参数有一些限制,只能是整型常量值 (包括枚举),指向对象/函数/成员的指针,指 向对象或函数的左值引用,或者 std::nullptr_t(nullptr 的类型)
  2. 浮点数和类型对象不能作为非模板参数
template<double VAT>  // 错误,不允许浮点作为非模板参数
double process (double v) { 
	return v * VAT; 
}

template<std::string name> // 错误,不允许类型对象作为非模板参数
class MyClass {
...
};

模板参数类型 auto

C++17 后,可以使用auto定义非类型模板参数,稍微改造一下之前的Stack代码即可:

// 其余和原来一样
template<typename T, auto Maxsize> class Stack {
public:
    using size_type = decltype(Maxsize); // 拿到真正的类型,方便后续的定义
}; 

当然,auto 关键字也无法突破 非类型模板参数本身的限制,比如如果auto推导成double,同样也会编译失败:

// 支持任意合法的非类型模板参数
template <auto Value>
void Print() {
    std::cout<<Value<<std::endl;
}

int main() {
    Print<1>(); // OK, Value 被推导为int
    // Print<"true">(); // No, Value 不能为字符串的字面量
    // Print<2.3>(); // No, Value 不能为浮点数
    static char const str[] = "hello"; // yes, 可以将静态字符数组传入非类型模板参数中
    Print<str>();  
}

总结

• 模板的模板参数可以是值,而非类型。
• 不能将浮点数或类类型对象作为非类型模板的参数。对于指向字符串字面量、临时对象和子
对象的指针/引用,有一些限制。
• 使用 auto 可使模板具有泛型值的非类型模板参数。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值