Mẫu <unsigned int N> nghĩa là gì?


121

Khi khai báo một mẫu, tôi đã quen với loại mã này:

template <class T>

Nhưng trong câu hỏi này , họ đã sử dụng:

template <unsigned int N>

Tôi đã kiểm tra xem nó có biên dịch không. Nhưng nó có nghĩa gì? Nó có phải là một tham số không phải kiểu không? Và nếu vậy, làm thế nào chúng ta có thể có một mẫu mà không có bất kỳ tham số kiểu nào?

Câu trả lời:


148

Hoàn toàn có thể tạo khuôn mẫu một lớp trên một số nguyên thay vì một kiểu. Chúng ta có thể gán giá trị được tạo mẫu cho một biến, hoặc xử lý nó theo cách chúng ta có thể làm với bất kỳ chữ số nguyên nào khác:

unsigned int x = N;

Trên thực tế, chúng tôi có thể tạo các thuật toán đánh giá tại thời điểm biên dịch (từ Wikipedia ):

template <int N>
struct Factorial 
{
     enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

11
Bạn cũng có thể sử dụng loại static constexpr intthay vì của bạn enum. Vì vậy, các Factorial<0>mẫu sẽ có static constexpr int value = 1, và template <int N> struct Factorialcó thể cóstatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo

@bobobobo điều này đã được trả lời trước C ++ 11 và constexpr.
Justin Meiners

154

Có, nó là một tham số không phải kiểu. Bạn có thể có một số loại thông số mẫu

  • Nhập Tham số.
    • Các loại
    • Mẫu (chỉ các lớp và mẫu bí danh, không có hàm hoặc mẫu biến)
  • Tham số không phải loại
    • Con trỏ
    • Người giới thiệu
    • Biểu thức hằng số tích phân

Những gì bạn có ở đó là loại cuối cùng. Đó là một hằng số thời gian biên dịch (cái gọi là biểu thức hằng số) và có kiểu số nguyên hoặc kiểu liệt kê. Sau khi tra cứu nó trong tiêu chuẩn, tôi phải chuyển các mẫu lớp lên phần loại - mặc dù các mẫu không phải là loại. Nhưng chúng được gọi là tham số kiểu với mục đích mô tả những kiểu đó. Bạn có thể có con trỏ (và cả con trỏ thành viên) và tham chiếu đến các đối tượng / chức năng có liên kết bên ngoài (những đối tượng có thể được liên kết đến từ các tệp đối tượng khác và có địa chỉ là duy nhất trong toàn bộ chương trình). Ví dụ:

Tham số loại mẫu:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Tham số số nguyên mẫu:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Tham số con trỏ mẫu (chuyển một con trỏ đến một hàm)

template<void (*F)()>
struct FunctionWrapper {
    static void call_it() { F(); }
};

// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;

Tham số tham chiếu mẫu (chuyển một số nguyên)

template<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Tham số mẫu mẫu.

template<template<typename T> class AllocatePolicy>
struct Pool {
    void allocate(size_t n) {
        int *p = AllocatePolicy<int>::allocate(n);
    }
};

// pass the template "allocator" as argument. 
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;

Một mẫu không có bất kỳ thông số nào là không thể. Nhưng một mẫu không có bất kỳ đối số rõ ràng nào là có thể - nó có các đối số mặc định:

template<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

Về mặt cú pháp, template<>được dành riêng để đánh dấu một chuyên môn hóa mẫu rõ ràng, thay vì một mẫu không có tham số:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};

Johannes, các mẫu có được gửi theo "loại" không? Tôi nghĩ chúng là những loại có thể được làm từ, nhưng không phải chính chúng?
sbi 27/12/10

@sbi xem giải thích: "Sau khi tra cứu nó trong tiêu chuẩn, tôi đã phải chuyển các mẫu lớp lên phần loại - mặc dù các mẫu không phải là loại. Nhưng chúng được gọi là tham số kiểu với mục đích mô tả các loại đó. ". Chú thích 126 ngày 14.1 / 2 nói như vậy. Nó chỉ là một phân loại được thực hiện để biến các tham số không phải kiểu khai báo giá trị / tham chiếu và tham số kiểu là thứ khai báo tên kiểu hoặc tên mẫu.
Johannes Schaub - litb

@ JohannesSchaub-litb vậy không có cách nào để nhập mẫu với let say std :: string? như lớp mẫu <std :: string S> với một số bộ đếm tĩnh trong đó để tạo id duy nhất cho mọi chuỗi khác nhau? băm chuỗi thành int sẽ là cách duy nhất không may phải không?
relaxxx

1
Tôi muốn thấy câu trả lời này được hoàn thành với các đối tượng thành viên lớp mẫu, tức là mẫu <typename C, typename R, typename P1, typename P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling

Đoạn mã với SillyExamplekhông thể được biên dịch bởi GCC 4.8.4. Lỗi đầu tiên là the value of ‘flag’ is not usable in a constant expression. Ngoài ra còn có các lỗi khác
HEKTO

17

Bạn tạo khuôn mẫu cho lớp của mình dựa trên một 'unsigned int'.

Thí dụ:

template <unsigned int N>
class MyArray
{
    public:
    private:
        double    data[N]; // Use N as the size of the array
};

int main()
{
    MyArray<2>     a1;
    MyArray<2>     a2;

    MyArray<4>     b1;

    a1 = a2;  // OK The arrays are the same size.
    a1 = b1;  // FAIL because the size of the array is part of the
              //      template and thus the type, a1 and b1 are different types.
              //      Thus this is a COMPILE time failure.
 }

15

Một lớp mẫu giống như một macro, chỉ khác là ít ác hơn rất nhiều.

Hãy nghĩ về một mẫu như một macro. Các tham số của mẫu được thay thế thành định nghĩa lớp (hoặc hàm), khi bạn xác định một lớp (hoặc hàm) bằng mẫu.

Sự khác biệt là các tham số có "kiểu" và các giá trị được truyền vào được kiểm tra trong quá trình biên dịch, giống như các tham số cho các hàm. Các kiểu hợp lệ là kiểu C ++ thông thường của bạn, như int và char. Khi bạn khởi tạo một lớp mẫu, bạn chuyển một giá trị của kiểu bạn đã chỉ định và trong một bản sao mới của định nghĩa lớp mẫu, giá trị này sẽ được thay thế ở bất cứ nơi nào tên tham số trong định nghĩa ban đầu. Cũng giống như một macro.

Bạn cũng có thể sử dụng " class" hoặc "typename " cho các tham số (chúng thực sự giống nhau). Với một tham số của một trong những kiểu này, bạn có thể chuyển một tên kiểu thay vì một giá trị. Giống như trước đây, ở mọi nơi, tên tham số nằm trong định nghĩa lớp mẫu, ngay khi bạn tạo một thể hiện mới, sẽ trở thành bất kỳ kiểu nào bạn truyền vào. Đây là cách sử dụng phổ biến nhất cho một lớp mẫu; Mọi người biết bất cứ điều gì về các mẫu C ++ đều biết cách làm điều này.

Hãy xem xét mã mẫu lớp mẫu này:

#include <cstdio>
template <int I>
class foo
{
  void print()
  {
    printf("%i", I);
  }
};

int main()
{
  foo<26> f;
  f.print();
  return 0;
}

Nó có chức năng giống như mã sử dụng macro này:

#include <cstdio>
#define MAKE_A_FOO(I) class foo_##I \
{ \
  void print() \
  { \
    printf("%i", I); \
  } \
};

MAKE_A_FOO(26)

int main()
{
  foo_26 f;
  f.print();
  return 0;
}

Tất nhiên, phiên bản mẫu an toàn hơn và linh hoạt hơn một tỷ lần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.