美文网首页C++ Templates
【C++ Templates(3)】非类型模板参数

【C++ Templates(3)】非类型模板参数

作者: downdemo | 来源:发表于2018-04-09 10:10 被阅读39次

非类型的类模板参数

// basics/stacknontype.hpp

#include <array>
#include <cassert>

template <typename T, std::size_t Maxsize>
class Stack {
private:
    std::array<T, Maxsize> elems;
    std::size_t numElems;
public:
    Stack();
    void push(const T& elem);
    void pop();
    const T& top() const;
    bool empty() const {
        return numElems == 0;
    }
    std::size_t size() const {
        return numElems;
    }
};

template<typename T, std::size_t Maxsize>
Stack<T, Maxsize>::Stack () : numElems(0) // 默认初始化元素数为0
{}

template<typename T, std::size_t Maxsize>
void Stack<T, Maxsize>::push (const T& elem)
{
    assert(numElems < Maxsize); // 确定Stack未满
    elems[numElems] = elem;
    ++numElems;
}

template<typename T, std::size_t Maxsize>
void Stack<T, Maxsize>::pop ()
{
    assert(!elems.empty());
    --numElems;
}


template<typename T, std::size_t Maxsize>
const T& Stack<T, Maxsize>::top () const
{
    assert(!elems.empty());
    return elems[numElems-1];  // return last element
}
  • 使用该模板需要同时指定类型和个数
// basics/stacknontype.cpp

#include "stacknontype.hpp"
#include <iostream>
#include <string>

int main()
{
    Stack<int,20> intStack; // 20个int的Stack
    intStack.push(1);
    std::cout << intStack.top() << '\n'; // 1
    intStack.pop();

    Stack<std::string, 40> stringStack;
    stringStack.push("hello");
    std::cout << stringStack.top() << '\n'; // hello
    stringStack.pop();
}
  • 非类型模板参数也可以指定默认值
template<typename T = int, std::size_t Maxsize = 100>
class Stack {
    …
};

非类型的函数模板参数

template<int Val, typename T>
T addValue (T x)
{
    return x + Val;
}
  • 这类模板通常用作参数
std::vector<int> v{ 5, 6, 7};
std::vector<int> v2(3);
std::transform(v.begin(), v.end(), v2.begin(), 
    addValue<1, int>);
for (auto x : v2) std::cout << x; // 678
  • 也能定义一个模板参数,由该参数之前的参数推断类型
template<auto Val, typename T = decltype(Val)>
T addValue(T x)
{
    return x + Val;
}
  • 或者保证传值类型相同
template<typename T, T Val = T{} >
T addValue(T x)
{
    return x + Val;
}

非类型模板参数的限制

  • 非类型模板参数可以是整型(包括枚举值)或指向外部链接对象的指针,但不能是浮点数和类对象
template<double Val> // 错误:非类型模板参数不能是浮点数
double process (double v)
{
    return v * Val;
}
 
template<std::string name> // 错误:非类型模板参数不能是类对象
class MyClass
{};
  • 也不能用字符串字面值常量,临时对象,数据成员或其他子对象作模板实参
template<const char* name>
class MyClass
{};
 
MyClass<"hello"> x;  // 错误:不允许字符串字面值常量作实参
// 使用指针也不行
const char* s = "hello";
const char s2[] = "hello";
MyClass<s> x; // 错误:s是internal linkage对象的指针
MyClass<s2> x; // 错误
  • C++的每个版本逐渐放宽了限制,C++11中对象有external linkage即可,C++14中对象有external linkage或internal linkage即可,C++17不需要linkage
// 以下任何用作模板实参的const char[]改为const char*都会出错
extern const char s03[] = "hello"; // external linkage
const char s11[] = "hello"; // internal linkage

int main()
{
    MyClass<s03> m03; // OK (all versions)
    MyClass<s11> m11; // OK since C++11
    static const char s17[] = "hi"; // no linkage
    MyClass<s17> m17; // OK since C++17
}
  • 非类型模板参数的实参可能是任何编译期表达式
template<int I, bool B>
class C
{};

C<sizeof(int) + 4, sizeof(int)==4> c;
  • 但要注意如果表达式中使用了大于号,必须把表达式写进小括号中,以防止嵌套的大于号被当作右尖括号结束实参列表
C<42, sizeof(int) > 4> c;    // ERROR: first > ends the template argument list
C<42, (sizeof(int) > 4)> c;  // OK

模板参数类型auto

  • C++17允许将非类型模板参数定义为auto,以接收任何被允许作为非类型模板参数的类型
// basics/stackauto.hpp

#include <array>
#include <cassert>

template<typename T, auto Maxsize>
class Stack {
public:
    using size_type = decltype(Maxsize);
private:
    std::array<T, Maxsize> elems;
    size_type numElems;
public:
    Stack();
    void push(const T& elem);
    void pop();
    const T& top() const;
    bool empty() const {
        return numElems == 0;
    }
    size_type size() const {
        return numElems;
    }
};

template<typename T, auto Maxsize>
Stack<T,Maxsize>::Stack () : numElems(0)
{}

template<typename T, auto Maxsize>
void Stack<T,Maxsize>::push (const T& elem)
{
    assert(numElems < Maxsize);
    elems[numElems] = elem;
    ++numElems;
}

template<typename T, auto Maxsize>
void Stack<T,Maxsize>::pop ()
{
    assert(!elems.empty());
    --numElems;
}

template<typename T, auto Maxsize>
const T& Stack<T,Maxsize>::top () const
{
    assert(!elems.empty());
    return elems[numElems-1];
}
  • C++14中允许auto作为返回类型
// 如果在类外定义size成员函数
template<typename T, auto Maxsize>
class Stack {
public:
    using size_type = decltype(Maxsize);
private:
    std::array<T, Maxsize> elems;
    size_type numElems;
public:
    size_type size() const;
};

template <typename T, auto Maxsize>
typename Stack<T, Maxsize>::size_type Stack<T, Maxsize>::size() const
{
    return numElems;
}

// C++14中可以写为
template<typename T, auto Maxsize>
class Stack {
public:
    using size_type = decltype(Maxsize);
private:
    std::array<T, Maxsize> elems;
    size_type numElems;
public:
    auto size() const;
};

template <typename T, auto Maxsize>
auto Stack<T, Maxsize>::size() const
{
    return numElems;
}

int main()
{
    Stack<int, 20u> intStack;
    Stack<std::string, 40> stringStack;
    auto size1 = intStack.size();
    auto size2 = stringStack.size();
    if (!std::is_same_v<decltype(size1), decltype(size2)>) {
        std::cout << "size types differ" << '\n';
    }
}
  • 但要注意,使用auto时非类型模板参数仍然不能是浮点数,auto只接收被允许作为非类型模板参数的类型
Stack<int, 3.14> sd; // 错误:非类型模板参数不能是浮点数
  • 此外比常规情况多出的一点限制是,auto不能接受const char[]
#include <iostream>

template<auto Val>
class MyClass {
public:
    void print() { std::cout << Val << '\n'; }
};

int main()
{
    MyClass<1> a;
    a.print(); // 1
    
    static const char s[] = "hello";
    MyClass<s> b; // 错误
    b.print();
}
  • 把auto改为decltype(auto),让decltype进行推断,即可解决此问题
template<decltype(auto) Val>
class MyClass {
public:
    void print() { std::cout << Val << '\n'; }
};

相关文章

  • 模板基础知识

    阅读经典——《C++ Templates》01 函数模板 类模板 非类型模板参数 一些技巧 模板代码的组织结构 一...

  • 【C++ Templates(3)】非类型模板参数

    非类型的类模板参数 使用该模板需要同时指定类型和个数 非类型模板参数也可以指定默认值 非类型的函数模板参数 这类模...

  • 03 非类型模板参数

    非类型的类模板参数 使用该模板需要同时指定类型和个数 非类型模板参数也可以指定默认值 非类型的函数模板参数 这类模...

  • 4/10

    1.模板非类型参数实现可变参数长度的模板数组如:template中int n为非类型参数...

  • C++中的数组类模板

    首先先了解一个小知识点:模板参数可以是数值型参数(非类型参数),例如这样: 这种数值型模板参数也有很多限制: 变量...

  • c++语法系列之7-- 函数模板

    (C++ Templates笔记) 1 函数模版的定义 说明:1)typename后面的T为模板参数,max后面的...

  • C++非类型模板参数

    一、非类型模板参数 简单点说就是在定义模板时,模板的类型参数不是一个泛型的 T,而是一个具体的类型。这种比较多的是...

  • C++11 模板元编程 - 目录

    前言 模板的基础知识模板的类型参数模板的默认参数模板的模板参数模板的特化模板的非类型参数模板的编译期计算数值计算类...

  • C++11 模板元编程 - 模板的编译期计算

    和函数求值类似,当模板的形参被实参替换时,模板会进行编译期计算。由于模板的参数支持类型参数和非类型参数,所以模板在...

  • C++11特性

    一、可变参数模板(Variadic Templates) 一、匿名函数(Lambda表达式) 二、可变模板参数 三...

网友评论

    本文标题:【C++ Templates(3)】非类型模板参数

    本文链接:https://www.haomeiwen.com/subject/wgubhftx.html