首頁 > 軟體

C++詳解非型別模板引數Nontype與Template及Parameters的使用

2022-06-24 10:00:23

非型別類別範本引數

前一章使用的例子 Stack 使用的是標準庫中的容器管理元素,也可以使用固定大小的 std::array,它的優勢是記憶體管理開銷更小,陣列的大小可以交給使用者指定。

#include <array>
#include <cassert>
template<typename T, std::size_t Maxsize>
class Stack {
  private:
    std::array<T,Maxsize> elems; // elements
    std::size_t numElems; // current number of elements
  public:
    Stack(); // constructor
    void push(T const& elem); // push element
    void pop(); // pop element
    T const& top() const; // return top element
    bool empty() const {  // return whether the stack is empty
      return numElems == 0;
    }
    std::size_t size() const { // return current number of elements
      return numElems;
    }
};
template<typename T, std::size_t Maxsize>
Stack<T,Maxsize>::Stack ()
 : numElems(0) // start with no elements
{
  // nothing else to do
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
  assert(numElems < Maxsize);
  elems[numElems] = elem; // append element
  ++numElems; // increment number of elements
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::pop ()
{
  assert(!elems.empty());
  --numElems; // decrement number of elements
}
template<typename T, std::size_t Maxsize>
T const& Stack<T,Maxsize>::top () const
{
  assert(!elems.empty());
  return elems[numElems-1]; // return last element
}
int main()
{
  Stack<int,20> int20Stack; // stack of up to 20 ints
  Stack<int,40> int40Stack; // stack of up to 40 ints
  Stack<std::string,40> stringStack; // stack of up to 40 strings
  // manipulate stack of up to 20 ints
  int20Stack.push(7);
  std::cout << int20Stack.top() << 'n';
  int20Stack.pop();
  // manipulate stack of up to 40 strings
  stringStack.push("hello");
  std::cout << stringStack.top() << 'n';
  stringStack.pop();
}

使用該模板需要同時指定型別和個數。 Maxsize 用於指定 std::array 的大小。非型別模板引數也可以有預設值。

template<typename T = int, std::size_t Maxsize = 100>
class Stack {
  ...
};

非型別函數模板引數

也可以為函數定義非型別模板引數。

template<int Val, typename T>
T addValue (T x)
{
  return x + Val;
}
std::transform (source.begin(), source.end(), // start and end of source
                dest.begin(), // start of destination
                addValue<5,int>); // operation

也可以指定一個模板引數,由該引數之前的引數推斷出其型別。

template<auto Val, typename T = decltype(Val)>
T foo();

或者保證傳值的型別和指定的型別相同。

template<typename T, T Val = T{}>
T bar();

非型別模板引數的限制

需要注意的是,非型別模板引數有一定的限制。一般地,非型別模板引數可以是整形(包括列舉)、指向物件/函數/成員的指標、物件/函數的左值參照或空指標型別 std::nullptr_t

浮點數和類型別物件不可以作為非型別模板引數。

template<double VAT>        // ERROR: floating-point values are not
double process (double v)   // allowed as template parameters
{
  return v * VAT;
}
template<std::string name>  // ERROR: class-type objects are not
class MyClass {             // allowed as template parameters
  ...
};

當使用指標或參照作為非型別模板引數時,不能用字串字面值、臨時物件、資料成員或其他子物件作模板實參。

template<char const* name>
class MyClass {
  ...
};
MyClass<"hello"> x; // ERROR: string literal "hello" not allowed

C++版本逐漸放寬了限制。C+11 之前,物件必須有外部連結;C++17 之前物件必須有外部或內部連結;C++17 放開了此限制。

extern char const s03[] = "hi"; // external linkage
char const s11[] = "hi"; // internal linkage
int main()
{
  MyClass<s03> m03; // OK (all versions)
  MyClass<s11> m11; // OK since C++11
  static char const 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,以接收任何允許作為非型別模板引數的型別。

#include <array>
#include <cassert>
template<typename T, auto Maxsize>
class Stack {
  public:
    using size_type = decltype(Maxsize);
  private:
    std::array<T,Maxsize> elems; // elements
    size_type numElems; // current number of elements
  public:
	Stack();  // constructor
	void push(T const& elem); // push element
	void pop(); // pop element
	T const& top() const; // return top element
	bool empty() const {  // return whether the stack is empty
	  return numElems == 0;
	}
	size_type size() const { // return current number of elements
	  return numElems;
	}
};
// constructor
template<typename T, auto Maxsize>
Stack<T,Maxsize>::Stack ()
 : numElems(0) // start with no elements
{
  // nothing else to do
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
  assert(numElems < Maxsize);
  elems[numElems] = elem; // append element
  ++numElems; // increment number of elements
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::pop ()
{
  assert(!elems.empty());
  --numElems; // decrement number of elements
}
template<typename T, auto Maxsize>
T const& Stack<T,Maxsize>::top () const
{
  assert(!elems.empty());
  return elems[numElems-1]; // return last element
}

從 C++14 開始,已經支援使用 auto 作為函數返回值。因此成員函數 size() 可以簡寫為:

auto size() const { // return current number of elements
  return numElems;
}

上述模板的使用:

int main()
{
  Stack<int,20u> int20Stack; // stack of up to 20 ints
  Stack<std::string, 40> stringStack; // stack of up to 40 strings
  // manipulate stack of up to 20 ints
  int20Stack.push(7);
  std::cout << int20Stack.top() << 'n';
  auto size1 = int20Stack.size();
  stringStack.push("hello");
  std::cout << stringStack.top() << 'n';
  auto size2 = stringStack.size();
  if (!std::is_same_v<decltype(size1), decltype(size2)>) {
    std::cout << "size types differ" << 'n';
  }
}

前面說過,非型別模板引數 auto 接收任何允許作為非型別模板引數的型別。

#include <iostream>
template<auto T> // take value of any possible nontype parameter (since C++17)
class Message {
  public:
   void print() {
     std::cout << T << 'n';
   }
};
int main()
{
  Message<42> msg1;
  msg1.print(); // initialize with int 42 and print that value:42
  static char const s[] = "hello";
  Message<s> msg2; // initialize with char const[6] "hello"
  msg2.print(); // and print that value:hello
}

非型別模板 auto 的引數仍不能是浮點數。

Stack<int,3.14> sd; // ERROR: Floating-point nontype argument

使用 decltype(auto) 指定非型別模板引數的型別也是可以的。

template<decltype(auto) N>
class C {
  ...
};
int i;
C<(i)> x; // N is int&

參考 http://www.tmplbook.com

到此這篇關於C++詳解非型別模板引數Nontype與Template及Parameters的使用的文章就介紹到這了,更多相關C++非型別模板引數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com