非类型模板参数

本文讨论了C++中模板参数的灵活性,包括非类型模板参数的适用场景,如std::array和std::tuple的使用,以及C++17中auto的引入如何增加泛化性。同时提到了非类型模板参数的限制,如不能用于浮点类型和类对象。
摘要由CSDN通过智能技术生成

        模板参数只能够是某种具体的类型么?答案是否定的。除了具体的类型,常规数据值也可以作为模板参数,如下:

#include <cstdio>
#include <cassert>
#include <functional>

template <typename T, size_t S>
class Array final
{
public:
    Array(void) : m_Data(new T[S]) {}
    
    T &operator[](size_t i) {
        assert(i < S);
        return m_Data[i];
    }
    
    size_t Size(void) const {
        return S;
    }
    
    Array(const Array &) = delete;
    Array(Array &&) = delete;
    Array &operator=(const Array &) = delete;
    Array &operator=(Array &&) = delete;
private:
    T *m_Data;
};

template <typename T, size_t S, typename F>
void Visit(Array<T, S> &array, F func)
{
    for (size_t i = 0; i < array.Size(); ++i) {
        func(array[i]);
    }
}

int main(int argc, char **argv)
{
    Array<int, 10> array;
    auto val_assign = [&](int &val) -> void {
        val = 10;
    };
    Visit(array, val_assign);
    
    auto val_print = [&](int &val) -> void {
        printf("%d\n", val);
    };
    Visit(array, val_print);
  
    return 0;
}

        使用非类型模板参数是有限制的。通常它们只能是整形常量(包含枚举),指向 objects/functions/members 的指针,objects 或者 functions 的左值引用,或者是 std::nullptr_t (类型是 nullptr)。下面的源码都是无法通过编译的:

template <double pi = 3.14>
double side(double radius)
{
    return pi * radius * radius;
}

template <std::string s>
std::string cat(std::string &str)
{
    return str + s;
}

template <std::string *p>
std::string print(void)
{
    printf("%s\n", *p->c_str());
}

        非类型模板参数在c++中的使用场景还是比较多的,例如:std::array,std::get(读取std::tuple)。

auto作为非模板类型参数的类型

        从C++17开始,可以使用auto代替非模板类型参数的具体类型,并且可以通过decltype对非模板类型参数的具体类型推断,然后使用,下面是一个简单的例子:

#include <vector>
#include <cstdio>
#include <cassert>

template <typename T, auto MAX_SIZE>
class MyArray final
{
private:
    using size_type = decltype(MAX_SIZE);
    
public:
    MyArray(void) : m_Data(new T[MAX_SIZE]), m_Index(0) {}
    
    ~MyArray(void) {
        if (m_Data == nullptr) {
            delete[] m_Data;
            m_Data = nullptr;
        }
    }
    
    void push(const T &data) {
        assert(m_Index < MAX_SIZE);
        m_Data[m_Index++] = data;
    }
    
    void pop(void) {
        assert(m_Index > 0);
        m_Index--;
    }
    
    T top(void) const {
        assert(m_Index > 0);
        return m_Data[m_Index - 1];
    }
    
    bool empty(void) const {
        return m_Index == 0;
    }
    
    size_type size(void) const {
        return m_Index;
    }
    
    MyArray(const MyArray &) = default;
    MyArray(MyArray &&) = default;
    MyArray &operator=(const MyArray &) = default;
    MyArray &operator=(MyArray &&) = default;
    
private:
    T *m_Data;
    size_type m_Index;
};

int main(int argc, char **argv)
{
    MyArray<int, 15> arr1;
    for (int i = 0; i < 15; ++i) {
        arr1.push(i);
    }
    
    MyArray<int, 100u> arr2;
    for (int i = 0; i < 100; ++i) {
        arr2.push(i);
    }
    return 0;
}

        关于上面的例子,如何确认arr1和arr2的非模板类型参数的类型是否相同?可以通过std::is_same::value(c++11之后)或std::is_same_v(c++17之后),代码如下:

if (std::is_same<decltype(arr1.size()), decltype(arr2.size())>::value) {
    printf("size type same !\n");
}
else {
    printf("size type diff");
}

        但需要注意,关于非类型模板参数的限制仍然存在:不可以将浮点型或者 class 类型的对象用于非类型模板参数。 

        非类型模板参数支持auto有什么意义?书中给出的答案,使得泛化更加灵活。下面的代码或许能够说明这一点:

template<unsigned char v>
void MyPrint(void) 
{
    printf("%u\n", v);
}

......
MyPrint<10>(); //OK
MyPrint<1000>(); //invalid explicitly-specified argument for template parameter 'v'


......
template<auto v>
void MyPrint2(void)
{
    printf("%u\n", v);
}

......
MyPrint2<10>();
MyPrint2<1000>();

        但是个人始终觉得这个说法有些勉强,非模板类型参数的类型设置为uint64_t,无论10,还是1000,都是用uint64_t,会有什么问题么? 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值