แม่แบบพารามิเตอร์มีประโยชน์อย่างไรบ้าง?


238

ฉันเคยเห็นตัวอย่างของ C ++ โดยใช้พารามิเตอร์แม่แบบแม่แบบ (นั่นคือแม่แบบที่ใช้แม่แบบเป็นพารามิเตอร์) เพื่อทำการออกแบบคลาสตามนโยบาย เทคนิคนี้มีประโยชน์อื่น ๆ อีกบ้าง?


4
ฉันมาจากทิศทางอื่น (FP, Haskell ฯลฯ ) และลงจอดบนสิ่งนี้: stackoverflow.com/questions/2565097/higher-kinded-types-with-c
Erik Kaplun

คำตอบ:


197

ฉันคิดว่าคุณต้องใช้ไวยากรณ์แม่แบบแม่แบบเพื่อส่งผ่านพารามิเตอร์ที่มีประเภทเป็นแม่แบบขึ้นอยู่กับแม่แบบอื่นเช่นนี้:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

นี่Hคือแม่แบบ Hแต่ฉันต้องการฟังก์ชั่นนี้เพื่อจัดการกับความเชี่ยวชาญทั้งหมดของ

หมายเหตุ : ฉันเขียนโปรแกรม c ++ มาหลายปีแล้วและต้องการเพียงครั้งเดียวเท่านั้น ฉันพบว่ามันเป็นคุณสมบัติที่ไม่ค่อยต้องการ (แน่นอนมีประโยชน์เมื่อคุณต้องการ!)

ฉันพยายามคิดถึงตัวอย่างที่ดีและบอกตามตรงว่าส่วนใหญ่ไม่จำเป็น แต่ขอยกตัวอย่าง ลองทำเป็นว่าstd::vector ไม่ได้typedef value_typeมี

แล้วคุณจะเขียนฟังก์ชันที่สามารถสร้างตัวแปรประเภทที่ถูกต้องสำหรับองค์ประกอบเวกเตอร์ได้อย่างไร สิ่งนี้จะได้ผล

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
    // This can be "typename V<T, A>::value_type",
    // but we are pretending we don't have it

    T temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

หมายเหตุ : std::vectorมีพารามิเตอร์เทมเพลตสองแบบประเภทและตัวจัดสรรดังนั้นเราจึงต้องยอมรับทั้งสองอย่าง โชคดีเนื่องจากการลดประเภทเราไม่จำเป็นต้องเขียนประเภทที่แน่นอนออกมาอย่างชัดเจน

ซึ่งคุณสามารถใช้เช่นนี้

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

หรือดีกว่าเราสามารถใช้:

f(v); // everything is deduced, f can deal with a vector of any type!

UPDATE : แม้เรื่องนี้จะเป็นตัวอย่างที่วางแผนไว้ในขณะที่ตัวอย่างเป็นไม่เป็นตัวอย่างที่น่าตื่นตาตื่นใจเนื่องจาก C ++ 11 autoแนะนำ ตอนนี้ฟังก์ชั่นเดียวกันสามารถเขียนได้เป็น:

template <class Cont>
void f(Cont &v) {

    auto temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

ซึ่งเป็นวิธีที่ฉันต้องการเขียนโค้ดประเภทนี้


1
ถ้า f เป็นฟังก์ชันที่กำหนดโดยผู้ใช้ของไลบรารีมันน่าเกลียดที่ผู้ใช้ต้องผ่าน std :: allocator <T> เป็นอาร์กิวเมนต์ ฉันคาดว่ารุ่นที่ไม่มีอาร์กิวเมนต์ std :: allocator จะทำงานโดยใช้พารามิเตอร์เริ่มต้นของ std :: vector มีการปรับปรุงใด ๆ ใน wrt C ++ 0x นี้หรือไม่?
amit

คุณไม่จำเป็นต้องให้ตัวจัดสรร สิ่งที่สำคัญคือพารามิเตอร์เทมเพลตเทมเพลตถูกกำหนดเหนือจำนวนอาร์กิวเมนต์ที่ถูกต้อง แต่ฟังก์ชั่นไม่ควรสนใจสิ่งที่ "ประเภท" หรือความหมายของพวกเขาต่อไปนี้ทำงานได้ดีใน C ++ 98:template<template<class, class> class C, class T, class U> void f(C<T, U> &v)
pfalcon

ผมสงสัยว่าทำไม instantiation เป็นและไม่ได้f<vector,int> f<vector<int>>
bobobobo

2
@bobobobo ทั้งสองมีความหมายต่างกัน f<vector,int>วิธีการf<ATemplate,AType>, f<vector<int>>วิธีการf<AType>
user362515

@phaedrus: (มากในภายหลัง ... ) จุดดีที่ดีขึ้นตัวอย่างเพื่อตรวจจัดสรรทั่วไปและตัวอย่างชัดเจนมากขึ้น :-)
Evan Teran

163

ที่จริงแล้ว usecase สำหรับพารามิเตอร์แม่แบบค่อนข้างชัดเจน เมื่อคุณเรียนรู้ว่า C ++ stdlib มีช่องโหว่ที่ไม่ได้กำหนดตัวดำเนินการส่งออกสตรีมสำหรับประเภทคอนเทนเนอร์มาตรฐานคุณจะต้องเขียนสิ่งต่อไปนี้:

template<typename T>
static inline std::ostream& operator<<(std::ostream& out, std::list<T> const& v)
{
    out << '[';
    if (!v.empty()) {
        for (typename std::list<T>::const_iterator i = v.begin(); ;) {
            out << *i;
            if (++i == v.end())
                break;
            out << ", ";
        }
    }
    out << ']';
    return out;
}

จากนั้นคุณจะเข้าใจว่ารหัสสำหรับเวกเตอร์นั้นเหมือนกันสำหรับ forward_list เหมือนกันจริงๆแล้วถึงแม้จะมีประเภทแผนที่จำนวนมากก็ยังคงเหมือนเดิม คลาสเทมเพลตเหล่านั้นไม่มีอะไรที่เหมือนกันยกเว้นสำหรับเมตาอินเตอร์เฟส / โปรโตคอลและการใช้พารามิเตอร์เท็มเพลตเทมเพลตช่วยให้สามารถจับภาพสามัญในทุกคลาสได้ ก่อนที่จะดำเนินการเขียนเทมเพลตคุณควรตรวจสอบการอ้างอิงเพื่อเรียกคืนคอนเทนเนอร์ลำดับที่ยอมรับอาร์กิวเมนต์เท็มเพลต 2 ตัวสำหรับประเภทค่าและตัวจัดสรร ในขณะที่ตัวจัดสรรถูกตั้งค่าเริ่มต้นเราก็ยังควรคำนึงถึงการมีอยู่ของมันในผู้ให้บริการเทมเพลตของเรา <<:

template<template <typename, typename> class Container, class V, class A>
std::ostream& operator<<(std::ostream& out, Container<V, A> const& v)
...

Voila ที่จะทำงานโดยอัตโนมัติสำหรับคอนเทนเนอร์ลำดับปัจจุบันและอนาคตทั้งหมดที่ปฏิบัติตามโปรโตคอลมาตรฐาน ในการเพิ่มแผนที่ลงในมิกซ์นั้นจะต้องดูที่การอ้างอิงเพื่อรับทราบว่าพวกเขายอมรับเทมเพลต 4 รายการดังนั้นเราจึงต้องการรุ่นโอเปอเรเตอร์ << ด้านบนด้วยเทมเพลตแม่แบบ 4-ARG นอกจากนี้เรายังเห็นว่า std: pair พยายามแสดงผลด้วยตัวดำเนินการ 2-ARG << สำหรับประเภทลำดับที่เรากำหนดไว้ก่อนหน้านี้ดังนั้นเราจะให้ความเชี่ยวชาญเฉพาะสำหรับ std :: pair

Btw ด้วย C + 11 ซึ่งอนุญาตเทมเพลต Variadic (และควรอนุญาตให้ใช้เท็มเพลตเทมเพลต Variadic) จึงเป็นไปได้ที่จะมีโอเปอเรเตอร์เดียว << เพื่อควบคุมทั้งหมด ตัวอย่างเช่น:

#include <iostream>
#include <vector>
#include <deque>
#include <list>

template<typename T, template<class,class...> class C, class... Args>
std::ostream& operator <<(std::ostream& os, const C<T,Args...>& objs)
{
    os << __PRETTY_FUNCTION__ << '\n';
    for (auto const& obj : objs)
        os << obj << ' ';
    return os;
}

int main()
{
    std::vector<float> vf { 1.1, 2.2, 3.3, 4.4 };
    std::cout << vf << '\n';

    std::list<char> lc { 'a', 'b', 'c', 'd' };
    std::cout << lc << '\n';

    std::deque<int> di { 1, 2, 3, 4 };
    std::cout << di << '\n';

    return 0;
}

เอาท์พุต

std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = float, C = vector, Args = <std::__1::allocator<float>>]
1.1 2.2 3.3 4.4 
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = char, C = list, Args = <std::__1::allocator<char>>]
a b c d 
std::ostream &operator<<(std::ostream &, const C<T, Args...> &) [T = int, C = deque, Args = <std::__1::allocator<int>>]
1 2 3 4 

9
นี่เป็นตัวอย่างที่ดีของพารามิเตอร์เทมเพลตเทมเพลตเนื่องจากมันแสดงให้เห็นถึงกรณีที่ทุกคนต้องจัดการ
Ravenwater

3
นี่เป็นคำตอบที่น่าตื่นตาที่สุดสำหรับฉันในเทมเพลต C ++ @WhozCraig คุณรู้จักรายละเอียดการขยายเทมเพลตได้อย่างไร
อรุณ

3
@Arun gcc สนับสนุนแมโครที่เรียกว่า__PRETTY_FUNCTION__ซึ่งเหนือสิ่งอื่นใดรายงานคำอธิบายพารามิเตอร์เทมเพลตในข้อความธรรมดา เสียงดังกราวทำมันเช่นกัน บางครั้งคุณสมบัติที่มีประโยชน์มากที่สุด (ตามที่คุณเห็น)
WhozCraig

20
พารามิเตอร์เทมเพลตเทมเพลตที่นี่ไม่ได้เป็นการเพิ่มค่าใด ๆ คุณอาจใช้พารามิเตอร์เทมเพลตปกติเป็นอินสแตนซ์ที่กำหนดของเทมเพลตคลาสได้เช่นกัน
David Stone

9
ฉันต้องเห็นด้วยกับเดวิดสโตน ไม่มีพารามิเตอร์ชี้ไปที่แม่แบบที่นี่ มันจะง่ายกว่าและมีประสิทธิภาพเท่าเทียมกันในการสร้างเทมเพลตธรรมดา (เทมเพลต <typename Container>) ฉันรู้ว่าโพสต์นี้ค่อนข้างเก่าดังนั้นฉันแค่เพิ่ม 2 เซ็นต์สำหรับคนที่สะดุดกับคำตอบนี้เพื่อหาข้อมูลเกี่ยวกับเทมเพลต
จิมวาร์โก

67

นี่คือตัวอย่างง่ายๆที่นำมาจาก'การออกแบบ Modern C ++ - การเขียนโปรแกรมและรูปแบบการออกแบบทั่วไปประยุกต์'โดย Andrei Alexandrescu:

เขาใช้คลาสที่มีพารามิเตอร์เท็มเพลตเทมเพลตเพื่อนำรูปแบบนโยบายไปใช้:

// Library code
template <template <class> class CreationPolicy>
class WidgetManager : public CreationPolicy<Widget>
{
   ...
};

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

ผลที่ได้คือรหัสลูกค้าสามารถใช้ 'WidgetManager' ได้อย่างสง่างาม:

typedef WidgetManager<MyCreationPolicy> MyWidgetMgr;

แทนที่จะเป็นวิธีที่ยุ่งยากและผิดพลาดมากกว่าที่คำจำกัดความที่ไม่มีอาร์กิวเมนต์เท็มเพลตเทมเพลตจะต้องมี:

typedef WidgetManager< MyCreationPolicy<Widget> > MyWidgetMgr;

1
คำถามที่ร้องขอมาเป็นพิเศษสำหรับตัวอย่างอื่นนอกเหนือจากรูปแบบนโยบาย
user2913094

ฉันมาถึงคำถามนี้อย่างแน่นอนจากหนังสือเล่มนี้ บันทึกที่คุ้มค่าคือว่าพารามิเตอร์แม่แบบแม่แบบนอกจากนี้ยังปรากฏในบท Typelist และรุ่นคลาสกับ Typelistsบท
วิกเตอร์

18

นี่เป็นอีกหนึ่งตัวอย่างในทางปฏิบัติจากฉันห้องสมุดเครือข่ายประสาท CUDA Convolutional ฉันมีแม่แบบชั้นเรียนต่อไปนี้:

template <class T> class Tensor

ซึ่งจริง ๆ แล้วใช้การจัดการเมทริกซ์ n มิติ นอกจากนี้ยังมีเทมเพลตสำหรับเด็ก:

template <class T> class TensorGPU : public Tensor<T>

ซึ่งใช้ฟังก์ชันการทำงานเดียวกัน แต่ใช้ GPU เทมเพลตทั้งสองสามารถทำงานกับประเภทพื้นฐานทั้งหมดเช่นโฟลต, double, int และอื่น ๆ และฉันยังมีเทมเพลตคลาส (ง่าย):

template <template <class> class TT, class T> class CLayerT: public Layer<TT<T> >
{
    TT<T> weights;
    TT<T> inputs;
    TT<int> connection_matrix;
}

เหตุผลที่นี่มีไวยากรณ์แม่แบบแม่แบบเพราะฉันสามารถประกาศการใช้งานของชั้นเรียน

class CLayerCuda: public CLayerT<TensorGPU, float>

ซึ่งจะมีทั้งน้ำหนักและอินพุตของประเภทลอยและบน GPU แต่ connection_matrix จะเป็น int เสมอไม่ว่าจะเป็นบน CPU (โดยระบุ TT = Tensor) หรือบน GPU (โดยระบุ TT = TensorGPU)


คุณสามารถบังคับให้การลด T ด้วยสิ่งต่าง ๆ เช่น: "template <class T, template <T> TT> CLayerT" และ "class CLayerCuda: public CLayerT <TensorGPU <float>>" หรือไม่? ในกรณีที่คุณไม่ต้องการ TT <otherT>
NicoBerrogorry

ไม่เคยคิด: เทมเพลต <เทมเพลต <คลาส T> คลาส U> คลาส B1 {}; จากibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/…จากการค้นหา google อย่างรวดเร็ว
NicoBerrogorry

12

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

template <typename DERIVED, typename VALUE> class interface {
    void do_something(VALUE v) {
        static_cast<DERIVED*>(this)->do_something(v);
    }
};

template <typename VALUE> class derived : public interface<derived, VALUE> {
    void do_something(VALUE v) { ... }
};

typedef interface<derived<int>, int> derived_t;

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

template <template <typename> class DERIVED, typename VALUE> class interface {
    void do_something(VALUE v) {
        static_cast<DERIVED<VALUE>*>(this)->do_something(v);
    }
};

template <typename VALUE> class derived : public interface<derived, VALUE> {
    void do_something(VALUE v) { ... }
};

typedef interface<derived, int> derived_t;

โปรดทราบว่าคุณกำลังกำจัดโดยตรงให้พารามิเตอร์แม่แบบอื่น ๆ เพื่อแม่แบบที่ได้รับ ; "อินเตอร์เฟซ" ยังคงได้รับพวกเขา

สิ่งนี้ยังช่วยให้คุณสร้าง typedefs ใน "ส่วนต่อประสาน" ที่ขึ้นอยู่กับพารามิเตอร์ประเภทซึ่งจะสามารถเข้าถึงได้จากเทมเพลตที่ได้รับ

typedef ด้านบนใช้งานไม่ได้เนื่องจากคุณไม่สามารถพิมพ์ไปยังเทมเพลตที่ไม่ระบุ วิธีนี้ใช้ได้ผล ((และ C ++ 11 มีการสนับสนุนดั้งเดิมสำหรับเทมเพลต typedefs):

template <typename VALUE>
struct derived_interface_type {
    typedef typename interface<derived, VALUE> type;
};

typedef typename derived_interface_type<int>::type derived_t;

คุณต้องมีหนึ่ง__image_typeสำหรับแต่ละการสร้างอินสแตนซ์ของเทมเพลตที่ได้รับมาอย่างน่าเสียดายเว้นแต่มีกลอุบายอื่น ๆ


ฉันต้องการโซลูชันที่แน่นอนนี้สำหรับโค้ดบางตัว (ขอบคุณ!) แม้ว่ามันจะใช้งานได้ แต่ฉันไม่เข้าใจว่าderivedจะใช้คลาสเทมเพลตได้อย่างไรโดยไม่มีอาร์กิวเมนต์ของเทมเพลตเช่นบรรทัดtypedef typename interface<derived, VALUE> type;
Carlton

@Carlton template <typename>มันทำงานโดยทั่วไปเพราะแม่แบบที่สอดคล้องกันพารามิเตอร์กำลังเต็มไปถูกกำหนดให้เป็น ในแง่หนึ่งคุณสามารถนึกถึงพารามิเตอร์เทมเพลตว่ามี 'metatype' เมตาไทป์ปกติสำหรับพารามิเตอร์เทมเพลตคือtypenameซึ่งหมายความว่าต้องกรอกด้วยประเภทปกติ templateวิธี metatype จะต้องเต็มไปด้วยการอ้างอิงถึงแม่แบบ derivedกำหนดเทมเพลตที่ยอมรับtypenameพารามิเตอร์ metatyped หนึ่งพารามิเตอร์ดังนั้นจึงเหมาะกับการเรียกเก็บเงินและสามารถอ้างอิงได้ที่นี่ ทำให้รู้สึก?
Mark McKenna

C ++ 11 ยังคงtypedefอยู่ นอกจากนี้คุณสามารถหลีกเลี่ยงสิ่งที่ซ้ำกันintในตัวอย่างแรกของคุณโดยใช้โครงสร้างมาตรฐานเช่น a value_typeในประเภท DERIVED
rubenvb

คำตอบนี้ไม่ได้ตั้งเป้าไว้ที่ C ++ 11; ฉันอ้างอิง C ++ 11 เพียงเพื่อบอกว่าคุณสามารถtypedefแก้ไขปัญหาได้จากบล็อก 2 แต่จุดที่ 2 นั้นถูกต้องฉันคิดว่า ... ใช่นั่นอาจเป็นวิธีที่ง่ายกว่าในการทำสิ่งเดียวกัน
Mark McKenna

7

นี่คือสิ่งที่ฉันวิ่งเข้าไป:

template<class A>
class B
{
  A& a;
};

template<class B>
class A
{
  B b;
};

class AInstance : A<B<A<B<A<B<A<B<... (oh oh)>>>>>>>>
{

};

สามารถแก้ไขได้:

template<class A>
class B
{
  A& a;
};

template< template<class> class B>
class A
{
  B<A> b;
};

class AInstance : A<B> //happy
{

};

หรือ (รหัสทำงาน):

template<class A>
class B
{
public:
    A* a;
    int GetInt() { return a->dummy; }
};

template< template<class> class B>
class A
{
public:
    A() : dummy(3) { b.a = this; }
    B<A> b;
    int dummy;
};

class AInstance : public A<B> //happy
{
public:
    void Print() { std::cout << b.GetInt(); }
};

int main()
{
    std::cout << "hello";
    AInstance test;
    test.Print();
}

4

ในการแก้ปัญหาด้วยเทมเพลต variadic ที่จัดทำโดย pfalcon ฉันพบว่ามันยากที่จะเชี่ยวชาญตัวดำเนินการ ostream สำหรับ std :: map เนื่องจากลักษณะโลภของความเชี่ยวชาญของ variadic นี่คือการแก้ไขเล็กน้อยที่ใช้งานได้สำหรับฉัน:

#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <map>

namespace containerdisplay
{
  template<typename T, template<class,class...> class C, class... Args>
  std::ostream& operator <<(std::ostream& os, const C<T,Args...>& objs)
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    for (auto const& obj : objs)
      os << obj << ' ';
    return os;
  }  
}

template< typename K, typename V>
std::ostream& operator << ( std::ostream& os, 
                const std::map< K, V > & objs )
{  

  std::cout << __PRETTY_FUNCTION__ << '\n';
  for( auto& obj : objs )
  {    
    os << obj.first << ": " << obj.second << std::endl;
  }

  return os;
}


int main()
{

  {
    using namespace containerdisplay;
    std::vector<float> vf { 1.1, 2.2, 3.3, 4.4 };
    std::cout << vf << '\n';

    std::list<char> lc { 'a', 'b', 'c', 'd' };
    std::cout << lc << '\n';

    std::deque<int> di { 1, 2, 3, 4 };
    std::cout << di << '\n';
  }

  std::map< std::string, std::string > m1 
  {
      { "foo", "bar" },
      { "baz", "boo" }
  };

  std::cout << m1 << std::endl;

    return 0;
}

2

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

#include <vector>

template <class T> class Alloc final { /*...*/ };

template <template <class T> class allocator=Alloc> class MyClass final {
  public:
    std::vector<short,allocator<short>> field0;
    std::vector<float,allocator<float>> field1;
};

2

มันช่วยปรับปรุงการอ่านรหัสของคุณให้ความปลอดภัยประเภทพิเศษและบันทึกความพยายามรวบรวม

สมมติว่าคุณต้องการพิมพ์แต่ละองค์ประกอบของคอนเทนเนอร์คุณสามารถใช้รหัสต่อไปนี้โดยไม่มีพารามิเตอร์แม่แบบแม่แบบ

template <typename T> void print_container(const T& c)
{
    for (const auto& v : c)
    {
        std::cout << v << ' ';
    }
    std::cout << '\n';
}

หรือด้วยพารามิเตอร์แม่แบบ

template< template<typename, typename> class ContainerType, typename ValueType, typename AllocType>
void print_container(const ContainerType<ValueType, AllocType>& c)
{
    for (const auto& v : c)
    {
        std::cout << v << ' ';
    }
    std::cout << '\n';
}

print_container(3)สมมติว่าคุณผ่านในจำนวนเต็มพูด สำหรับกรณีก่อนหน้าเทมเพลตจะถูกสร้างอินสแตนซ์โดยคอมไพเลอร์ซึ่งจะบ่นเกี่ยวกับการใช้งานของcใน for for loop ส่วนหลังจะไม่สร้างอินสแตนซ์ของเทมเพลตเลยเนื่องจากไม่พบประเภทการจับคู่

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


1

ฉันใช้สำหรับรุ่นที่กำหนดเวอร์ชันแล้ว

หากคุณมีประเภทเวอร์ชันผ่านเทมเพลตเช่นMyType<version>คุณสามารถเขียนฟังก์ชันที่คุณสามารถจับหมายเลขเวอร์ชันได้:

template<template<uint8_t> T, uint8_t Version>
Foo(const T<Version>& obj)
{
    assert(Version > 2 && "Versions older than 2 are no longer handled");
    ...
    switch (Version)
    {
    ...
    }
}

ดังนั้นคุณสามารถทำสิ่งต่าง ๆ ได้ขึ้นอยู่กับรุ่นของประเภทที่ส่งผ่านแทนที่จะมีโอเวอร์โหลดสำหรับแต่ละประเภท นอกจากนี้คุณยังสามารถมีฟังก์ชั่นการแปลงที่รับMyType<Version>และส่งคืนMyType<Version+1>ในแบบทั่วไปและแม้แต่เรียกคืนพวกเขาเพื่อให้มีToNewest()ฟังก์ชั่นที่ส่งคืนเวอร์ชันล่าสุดของประเภทจากเวอร์ชันเก่า ๆ (มีประโยชน์มากสำหรับบันทึกที่อาจถูกจัดเก็บในขณะที่ย้อนหลัง แต่ต้องถูกประมวลผลด้วยเครื่องมือใหม่ล่าสุดของวันนี้)

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