เทมเพลต <unsigned int N> หมายถึงอะไร


121

เมื่อประกาศเทมเพลตฉันคุ้นเคยกับการมีรหัสประเภทนี้:

template <class T>

แต่ในคำถามนี้พวกเขาใช้:

template <unsigned int N>

ฉันตรวจสอบว่าคอมไพล์ แต่มันหมายความว่าอย่างไร? เป็นพารามิเตอร์ที่ไม่ใช่ประเภทหรือไม่? และถ้าเป็นเช่นนั้นเราจะมีเทมเพลตโดยไม่มีพารามิเตอร์ประเภทใด ๆ ได้อย่างไร?

คำตอบ:


148

เป็นไปได้อย่างสมบูรณ์แบบที่จะเทมเพลตชั้นเรียนเป็นจำนวนเต็มมากกว่าประเภท เราสามารถกำหนดค่าเท็มเพลตให้กับตัวแปรหรือจัดการกับค่านี้ด้วยวิธีที่เราอาจใช้กับลิเทอรัลจำนวนเต็มอื่น ๆ :

unsigned int x = N;

ในความเป็นจริงเราสามารถสร้างอัลกอริทึมที่ประเมินเวลารวบรวม (จาก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
คุณยังสามารถใช้ type static constexpr intแทนenumไฟล์. ดังนั้นFactorial<0>แม่แบบจะมีstatic constexpr int value = 1และtemplate <int N> struct Factorialสามารถมีได้static constexpr int value = N * Factorial<N - 1>::value;
bobobobo

@bobobobo คำตอบนี้ก่อน C ++ 11 และconstexpr.
Justin Meiners

154

ใช่มันเป็นพารามิเตอร์ที่ไม่ใช่ประเภท คุณสามารถมีพารามิเตอร์เทมเพลตได้หลายประเภท

  • พิมพ์พารามิเตอร์
    • ประเภท
    • เทมเพลต (เฉพาะคลาสและเทมเพลตนามแฝงไม่มีฟังก์ชันหรือเทมเพลตตัวแปร)
  • พารามิเตอร์ที่ไม่ใช่ประเภท
    • ตัวชี้
    • อ้างอิง
    • นิพจน์ค่าคงที่อินทิกรัล

สิ่งที่คุณมีคือชนิดสุดท้าย เป็นค่าคงที่เวลาคอมไพล์ (เรียกว่านิพจน์คงที่) และเป็นจำนวนเต็มหรือการแจงนับชนิด หลังจากค้นหาในมาตรฐานแล้วฉันต้องย้ายเทมเพลตคลาสขึ้นไปในส่วนประเภทแม้ว่าเทมเพลตจะไม่ใช่ประเภทก็ตาม แต่จะเรียกว่าพารามิเตอร์ประเภทเพื่อวัตถุประสงค์ในการอธิบายชนิดเหล่านั้นอย่างไรก็ตาม คุณสามารถมีพอยน์เตอร์ (และพอยน์เตอร์สำหรับสมาชิก) และการอ้างอิงอ็อบเจ็กต์ / ฟังก์ชันที่มีการเชื่อมโยงภายนอก (ที่สามารถเชื่อมโยงจากอ็อบเจ็กต์ไฟล์อื่น ๆ และแอดเดรสที่ไม่ซ้ำกันในโปรแกรมทั้งหมด) ตัวอย่าง:

พารามิเตอร์ประเภทเทมเพลต:

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

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

พารามิเตอร์จำนวนเต็มของเทมเพลต:

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

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

พารามิเตอร์ตัวชี้เทมเพลต (ส่งตัวชี้ไปยังฟังก์ชัน)

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;

พารามิเตอร์อ้างอิงเทมเพลต (ผ่านจำนวนเต็ม)

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

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

พารามิเตอร์เทมเพลตเทมเพลต

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;

เทมเพลตที่ไม่มีพารามิเตอร์เป็นไปไม่ได้ แต่เทมเพลตที่ไม่มีอาร์กิวเมนต์ชัดเจนเป็นไปได้ - มีอาร์กิวเมนต์เริ่มต้น:

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

Vector<> test;

ในทางไวยากรณ์template<>สงวนไว้เพื่อทำเครื่องหมายความเชี่ยวชาญพิเศษของเทมเพลตอย่างชัดเจนแทนที่จะเป็นเทมเพลตที่ไม่มีพารามิเตอร์:

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

Johannes แม่แบบอยู่ภายใต้ "ประเภท" หรือไม่ ฉันคิดว่ามันเป็นประเภทอะไรที่สามารถสร้างขึ้นได้ แต่ไม่ใช่ประเภทของตัวเอง?
sbi

@sbi ดูคำอธิบาย: "หลังจากค้นหาในมาตรฐานแล้วฉันต้องย้ายเทมเพลตคลาสขึ้นไปในส่วน types - แม้ว่าเทมเพลตจะไม่ใช่ประเภท แต่จะเรียกว่า type-parameters เพื่อวัตถุประสงค์ในการอธิบายประเภทเหล่านั้นอย่างไรก็ตาม " เชิงอรรถ 126 ในวันที่ 14.1 / 2 กล่าวเช่นนั้น เป็นเพียงการจัดประเภทที่สร้างขึ้นเพื่อสร้างพารามิเตอร์ที่ไม่ใช่ประเภทซึ่งประกาศค่า / การอ้างอิงและพารามิเตอร์ประเภทเป็นสิ่งที่ประกาศชื่อประเภทหรือชื่อเทมเพลต
Johannes Schaub - litb

@ JohannesSchaub-litb ดังนั้นจึงไม่มีวิธีที่จะพิมพ์ template โดยบอกว่า std :: string? เช่น template <std :: string S> class ที่มีตัวนับแบบคงที่เพื่อสร้าง id เฉพาะสำหรับทุกสตริงที่แตกต่างกัน? การแฮชสตริงเป็น int จะเป็นวิธีเดียวที่น่าเสียดายใช่ไหม
relaxxx

1
ฉันชอบที่จะเห็นคำตอบนี้พร้อมกับออบเจ็กต์สมาชิกคลาสเทมเพลตเช่น template <typename C, typename R, typename P1, typename P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling

SillyExampleGCC 4.8.4 ไม่สามารถคอมไพล์โค้ดได้ ข้อผิดพลาดแรกคือthe value of ‘flag’ is not usable in a constant expression. มีข้อผิดพลาดอื่น ๆ เช่นกัน
HEKTO

17

คุณทำเทมเพลตของชั้นเรียนตาม 'int ที่ไม่ได้ลงนาม'

ตัวอย่าง:

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

คลาสเทมเพลตเปรียบเสมือนมาโครมี แต่ความชั่วร้ายน้อยกว่ามาก

คิดว่าแม่แบบเป็นมาโคร พารามิเตอร์ของเทมเพลตจะถูกแทนที่เป็นนิยามคลาส (หรือฟังก์ชัน) เมื่อคุณกำหนดคลาส (หรือฟังก์ชัน) โดยใช้เทมเพลต

ความแตกต่างคือพารามิเตอร์มี "ประเภท" และค่าที่ส่งผ่านจะถูกตรวจสอบระหว่างการคอมไพล์เช่นพารามิเตอร์ไปยังฟังก์ชัน ประเภทที่ถูกต้องคือประเภท C ++ ปกติของคุณเช่น int และ char เมื่อคุณสร้างอินสแตนซ์คลาสเทมเพลตคุณจะส่งผ่านค่าของชนิดที่คุณระบุและในสำเนาใหม่ของนิยามคลาสเทมเพลตค่านี้จะถูกแทนที่ในทุกที่ที่ชื่อพารามิเตอร์อยู่ในนิยามดั้งเดิม เช่นเดียวกับมาโคร

คุณยังสามารถใช้ประเภท " class" หรือ " typename" สำหรับพารามิเตอร์ (ซึ่งเหมือนกันจริงๆ) ด้วยพารามิเตอร์ประเภทใดประเภทหนึ่งเหล่านี้คุณสามารถส่งชื่อประเภทแทนค่าได้ เช่นเดียวกับก่อนหน้านี้ทุกที่ที่ชื่อพารามิเตอร์อยู่ในนิยามคลาสเทมเพลตทันทีที่คุณสร้างอินสแตนซ์ใหม่จะกลายเป็นประเภทใดก็ตามที่คุณส่งผ่าน นี่คือการใช้งานทั่วไปสำหรับคลาสเทมเพลต ทุกคนที่รู้ทุกอย่างเกี่ยวกับเทมเพลต C ++ จะรู้วิธีดำเนินการนี้

พิจารณาโค้ดตัวอย่างคลาสเทมเพลตนี้:

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

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

มันทำงานเหมือนกับโค้ดที่ใช้มาโครนี้:

#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;
}

แน่นอนว่าเวอร์ชันเทมเพลตปลอดภัยและยืดหยุ่นกว่าเป็นพันล้านเท่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.