การจัดเก็บนิยามฟังก์ชันเท็มเพลต C ++ ในไฟล์. CPP


526

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

ไฟล์. h

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

ไฟล์. cpp

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

หมายเหตุสองบรรทัดสุดท้าย - ฟังก์ชั่นเทมเพลต foo :: do ใช้กับสตริง ints และ std :: เท่านั้นดังนั้นคำจำกัดความเหล่านั้นจึงหมายความว่าแอปจะลิงก์

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


22
ฉันไม่รู้ว่ามันเป็นไปได้ - เป็นเคล็ดลับที่น่าสนใจ! มันจะช่วยให้งานที่ผ่านมาจำนวนมากรู้เรื่องนี้ - ไชโย!
Xan

69
สิ่งที่ stomps ฉันคือการใช้doเป็นตัวบ่งชี้: p
Quentin

ฉันทำ somerhing คล้ายกับ gcc แต่ยังคงค้นคว้า
Nick

16
นี่ไม่ใช่ "แฮ็ค" มันเป็น decleration ไปข้างหน้า นี่คือสถานที่ในมาตรฐานของภาษา; ใช่แล้วมันได้รับอนุญาตในคอมไพเลอร์ตามมาตรฐานทุกตัว
Ahmet Ipkin

1
ถ้าคุณมีวิธีการมากมาย คุณสามารถทำtemplate class foo<int>;template class foo<std::string>;ตอนท้ายไฟล์. cpp ได้หรือไม่
โง่เขลา

คำตอบ:


231

ปัญหาที่คุณอธิบายสามารถแก้ไขได้โดยการกำหนดแม่แบบในส่วนหัวหรือผ่านวิธีการที่คุณอธิบายข้างต้น

ฉันแนะนำให้อ่านประเด็นต่อไปนี้จากC ++ FAQ Lite :

พวกเขามีรายละเอียดมากมายเกี่ยวกับปัญหาเทมเพลตเหล่านี้ (และอื่น ๆ )


39
ลิงก์ที่อ้างถึงตอบคำถามในเชิงบวกคือสามารถทำสิ่งที่ Rob แนะนำและมีรหัสให้พกพาได้
ivotron

161
คุณสามารถโพสต์ชิ้นส่วนที่เกี่ยวข้องในคำตอบนั้นได้หรือไม่? เหตุใดการอ้างอิงดังกล่าวถึงได้รับอนุญาตบน SO ฉันไม่รู้ว่าจะหาอะไรในลิงค์นี้เนื่องจากมันเปลี่ยนไปมาก
ระบุ

124

สำหรับคนอื่น ๆ ในหน้านี้สงสัยว่าไวยากรณ์ที่ถูกต้องคืออะไร (เช่นเดียวกับฉัน) สำหรับความเชี่ยวชาญเทมเพลตอย่างชัดเจน (หรืออย่างน้อยใน VS2008) มันมีดังต่อไปนี้ ...

ในไฟล์. h ของคุณ ...

template<typename T>
class foo
{
public:
    void bar(const T &t);
};

และในไฟล์. cpp ของคุณ

template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;

15
คุณหมายถึง "for specialiastion template CLASS หรือไม่" ในกรณีนั้นจะครอบคลุมทุกฟังก์ชั่นที่คลาส templated มีหรือไม่
อาเธอร์

@ ดูเหมือนว่าฉันไม่ได้ฉันมีวิธีการแม่แบบอยู่ในส่วนหัวและวิธีการอื่น ๆ ส่วนใหญ่ใน CPP ทำงานได้ดี ทางออกที่ดีมาก
user1633272

ในกรณีของผู้ถามพวกเขามีแม่แบบฟังก์ชันไม่ใช่แม่แบบชั้นเรียน
user253751

23

รหัสนี้มีรูปแบบที่ดี คุณจะต้องใส่ใจว่าคำจำกัดความของเทมเพลตสามารถมองเห็นได้ที่จุดเริ่มต้น หากต้องการอ้างอิงมาตรฐาน§ 14.7.2.4:

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


2
สิ่งที่ไม่ส่งออกหมายถึงอะไร
Dan Nissenbaum

1
@Dan มองเห็นได้เฉพาะภายในหน่วยรวบรวมของมันไม่ใช่ด้านนอก หากคุณเชื่อมโยงหน่วยการคอมไพล์หลาย ๆ ตัวเข้าด้วยกันสัญลักษณ์ที่ส่งออกสามารถใช้ข้ามพวกมันได้ (และจะต้องมีอย่างน้อยหนึ่งอย่างหรืออย่างน้อยก็ในกรณีของเทมเพลตคำจำกัดความที่สอดคล้องกัน
Konrad Rudolph

ขอบคุณ ฉันคิดว่าฟังก์ชั่นทั้งหมด (โดยค่าเริ่มต้น) สามารถมองเห็นได้นอกหน่วยรวบรวม หากฉันมีสองหน่วยรวบรวมa.cpp(กำหนดฟังก์ชั่นa() {}) และb.cpp(กำหนดฟังก์ชั่นb() { a() }) แล้วนี้จะประสบความสำเร็จในการเชื่อมโยง ถ้าฉันถูกต้องแล้วข้อความข้างต้นดูเหมือนจะไม่ใช้สำหรับกรณีทั่วไป ... ฉันจะไปไหนผิดหรือเปล่า?
Dan Nissenbaum

@ ด่านตัวอย่างเล็กน้อย: inlineฟังก์ชั่น
Konrad Rudolph

1
ฟังก์ชั่น @Dan inlineแม่แบบโดยปริยาย เหตุผลที่ไม่มีมาตรฐาน C ++ ABI มันเป็นเรื่องยาก / เป็นไปไม่ได้ที่จะกำหนดเอฟเฟกต์ที่สิ่งนี้จะมี
Konrad Rudolph

15

ควรทำงานได้ดีทุกที่ที่มีเทมเพลตที่รองรับ การสร้างแม่แบบที่ชัดเจนเป็นส่วนหนึ่งของมาตรฐาน C ++


13

ตัวอย่างของคุณถูกต้อง แต่ไม่สามารถพกพาได้ นอกจากนี้ยังมีไวยากรณ์ที่สะอาดขึ้นเล็กน้อยที่สามารถใช้ได้ (ดังที่ระบุไว้โดย @ namespace-sid)

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

วิธีอื่นคือความผันแปรเล็กน้อยในสิ่งที่คุณมี: เพิ่มไฟล์ที่สามซึ่งเป็นไฟล์การนำไปใช้งาน / การสร้างอินสแตนซ์

ไฟล์ foo.h

// Standard header file guards omitted

template <typename T>
class foo
{
public:
    void bar(const T& t);
};

ไฟล์ foo.cpp

// Always include your headers
#include "foo.h"

template <typename T>
void foo::bar(const T& t)
{
    // Do something with t
}

ไฟล์ foo-impl.cpp

// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;

ข้อแม้หนึ่งคือคุณต้องบอกให้คอมไพเลอร์คอมไพล์foo-impl.cppแทนการคอมไพล์foo.cppหลังไม่ได้ทำอะไรเลย

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

สิ่งนี้ช่วยให้มีความยืดหยุ่นมากขึ้นเมื่อแบ่งปันคลาส templated สำหรับการใช้งานอื่น

การตั้งค่านี้ยังช่วยลดเวลาในการคอมไพล์สำหรับคลาสที่นำกลับมาใช้ใหม่เนื่องจากคุณไม่ได้ทำการคอมไพล์ไฟล์ส่วนหัวซ้ำกันในแต่ละหน่วยการแปล


สิ่งนี้ซื้อให้คุณ คุณยังคงต้องแก้ไข foo-impl.cpp เพื่อเพิ่มความเชี่ยวชาญใหม่
MK

การแยกรายละเอียดการนำไปปฏิบัติ (คำจำกัดความที่รู้จักกันในfoo.cpp) ซึ่งเวอร์ชันใดที่ถูกรวบรวม (ในfoo-impl.cpp) และการประกาศ (ในfoo.h) ฉันไม่ชอบว่าแม่แบบ C ++ ส่วนใหญ่จะถูกกำหนดในไฟล์ส่วนหัวทั้งหมด นั่นคือการนับตามมาตรฐาน C / C ++ ของคู่c[pp]/hสำหรับแต่ละคลาส / เนมสเปซ / การจัดกลุ่มใดก็ตามที่คุณใช้ คนดูเหมือนจะยังคงใช้ไฟล์ส่วนหัวเสาหินเพียงเพราะทางเลือกนี้ไม่ได้ใช้กันอย่างแพร่หลายหรือเป็นที่รู้จัก
คาเมรอน Tacklind

1
@MK ฉันวางอินสแตนซ์ของแม่แบบที่ชัดเจนในตอนท้ายของคำนิยามในไฟล์ต้นฉบับในตอนแรกจนกระทั่งฉันต้องการอินสแตนซ์เพิ่มเติมที่อื่น (เช่นการทดสอบหน่วยโดยใช้แบบจำลองเป็นเทมเพลต) การแยกนี้ทำให้ฉันสามารถเพิ่มอินสแตนซ์ได้มากขึ้นจากภายนอก ยิ่งกว่านั้นมันยังคงทำงานได้เมื่อฉันเก็บต้นฉบับไว้เป็นh/cppคู่แม้ว่าฉันจะต้องล้อมรอบรายการอินสแตนซ์ดั้งเดิมในตัวป้องกันรวมถึง แต่ฉันก็ยังสามารถรวบรวมได้foo.cppตามปกติ ฉันยังค่อนข้างใหม่กับ C ++ และจะสนใจทราบว่าการใช้งานแบบผสมนี้มีข้อแม้เพิ่มเติมหรือไม่
Thirdwater

3
ฉันคิดว่ามันเป็นที่นิยมในการแยกและfoo.cpp foo-impl.cppอย่า#include "foo.cpp"อยู่ในfoo-impl.cppไฟล์; แทนที่จะเพิ่มประกาศextern template class foo<int>;ไปเพื่อป้องกันไม่ให้คอมไพเลอร์จากอินสแตนซ์แม่แบบเมื่อรวบรวมfoo.cpp foo.cppตรวจสอบให้แน่ใจว่าระบบ build สร้างทั้ง .cppไฟล์และส่งผ่านทั้งสองไฟล์อ็อบเจ็กต์ไปยังตัวลิงก์ สิ่งนี้มีประโยชน์หลายประการ: ก) ชัดเจนfoo.cppว่าไม่มีการสร้างอินสแตนซ์ b) การเปลี่ยนแปลง foo.cpp ไม่จำเป็นต้องมีการคอมไพล์ใหม่ของ foo-impl.cpp
Shmuel Levine

3
นี่เป็นวิธีที่ดีมากในการแก้ไขปัญหาของคำจำกัดความของแม่แบบที่ดีที่สุดทั้งในส่วนของโลก - การติดตั้งส่วนหัวและการสร้างอินสแตนซ์สำหรับประเภทที่ใช้บ่อย การเปลี่ยนแปลงเดียวที่ฉันจะทำให้การติดตั้งนี้คือการเปลี่ยนชื่อfoo.cppเข้ามาfoo_impl.hและเข้าไปเพียงfoo-impl.cpp foo.cppฉันยังจะเพิ่ม typedefs สำหรับ instantiations จากfoo.cppไปเช่นเดียวกันfoo.h using foo_int = foo<int>;เคล็ดลับคือการให้ส่วนต่อประสานสองส่วนกับผู้ใช้เพื่อเป็นทางเลือก instantiation เขารวมถึงเมื่อต้องการของผู้ใช้ที่กำหนดไว้ล่วงหน้าเมื่อผู้ใช้ต้องการบางสิ่งบางอย่างออกคำสั่งเขารวมถึงfoo.h foo_impl.h
Wormer

5

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


คุณแน่ใจหรือไม่ว่าจะทำลาย ODR หากบรรทัดอินสแตนซ์ใน TemplateClassInst.cpp อ้างถึงไฟล์ต้นฉบับที่เหมือนกัน (มีคำจำกัดความฟังก์ชันเทมเพลต) นั่นไม่รับประกันว่าจะไม่ละเมิด ODR เนื่องจากคำจำกัดความทั้งหมดเหมือนกัน (แม้ว่าจะซ้ำกัน)?
Dan Nissenbaum

กรุณา ODR คืออะไร?
ไม่สามารถเอาคืนได้

4

มีอยู่ในมาตรฐานล่าสุดคือคำหลัก ( export) ที่จะช่วยบรรเทาปัญหานี้ แต่ก็ไม่ได้นำไปใช้กับคอมไพเลอร์ที่ฉันรับรู้นอกเหนือจาก Comeau

ดูคำถามที่พบบ่อยเกี่ยวกับเรื่องนี้


2
AFAIK การส่งออกจะตายเนื่องจากพวกเขากำลังเผชิญกับปัญหาที่ใหม่กว่าและใหม่กว่าทุกครั้งที่พวกเขาแก้ไขปัญหาสุดท้ายทำให้การแก้ปัญหาโดยรวมมีความซับซ้อนมากขึ้น และคำหลัก "ส่งออก" จะไม่ช่วยให้คุณสามารถ "ส่งออก" จาก CPP ได้ (จากของ H. Sutter อยู่ดี) ดังนั้นฉันจะพูดว่า: อย่ากลั้นลมหายใจของคุณ ...
paercebal

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

2
... และมันหายไปจากมาตรฐานเนื่องจากความซับซ้อนที่มากเกินไปเพื่อให้ได้รับน้อยที่สุด
DevSolar

4

นั่นเป็นวิธีมาตรฐานในการกำหนดฟังก์ชั่นเทมเพลต ฉันคิดว่ามีสามวิธีที่ฉันอ่านเพื่อกำหนดแม่แบบ หรืออาจเป็น 4. แต่ละคนมีข้อดีข้อเสีย

  1. กำหนดในคำจำกัดความของชั้นเรียน ฉันไม่ชอบสิ่งนี้เลยเพราะฉันคิดว่าคำจำกัดความของชั้นเรียนมีไว้เพื่ออ้างอิงอย่างเคร่งครัดและควรอ่านง่าย อย่างไรก็ตามมันมีความยุ่งยากน้อยกว่ามากในการกำหนดแม่แบบในชั้นเรียนนอก และไม่ใช่การประกาศเทมเพลตทั้งหมดที่อยู่ในระดับความซับซ้อนเดียวกัน วิธีนี้ยังทำให้แม่แบบเป็นแม่แบบที่แท้จริง

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

  3. รวมส่วนหัว. h และการนำไปปฏิบัติ CPP ใน main.CPP ของคุณ ฉันคิดว่านั่นเป็นวิธีที่ทำได้ คุณไม่ต้องเตรียมอินสแตนซ์ล่วงหน้าใด ๆ มันจะทำตัวเหมือนเทมเพลตจริง ปัญหาที่ฉันมีกับมันคือมันไม่เป็นธรรมชาติ ปกติแล้วเราจะไม่รวมและคาดว่าจะรวมไฟล์ต้นฉบับ ฉันเดาว่าเนื่องจากคุณรวมไฟล์ต้นฉบับไว้แล้วฟังก์ชันเทมเพลตก็สามารถแทรกเข้ามาได้

  4. วิธีสุดท้ายนี้ซึ่งเป็นวิธีการโพสต์กำหนดแม่แบบในไฟล์ต้นฉบับเช่นเดียวกับหมายเลข 3 แต่แทนที่จะรวมไฟล์ต้นฉบับไว้เราจะสร้างอินเทอร์เฟซให้กับเทมเพลตที่เราต้องการ ฉันไม่มีปัญหากับวิธีนี้และบางครั้งมันก็มีประโยชน์ เรามีหนึ่งรหัสขนาดใหญ่มันไม่สามารถได้รับประโยชน์จากการถูก inline ดังนั้นเพียงแค่ใส่มันในไฟล์ CPP และถ้าเรารู้อินสแตนซ์ทั่วไปและเราสามารถกำหนดไว้ล่วงหน้าได้ สิ่งนี้ช่วยเราจากการเขียนโดยพื้นฐานสิ่งเดียวกัน 5, 10 ครั้ง วิธีนี้มีประโยชน์ในการรักษารหัสของเราไว้ แต่ฉันไม่แนะนำให้ใส่ฟังก์ชั่นเล็ก ๆ ที่ใช้เป็นประจำในไฟล์ CPP เช่นนี้จะลดประสิทธิภาพของห้องสมุดของคุณ

หมายเหตุฉันไม่ได้ตระหนักถึงผลที่ตามมาของไฟล์ obj ป่อง


3

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

แก้ไข: แก้ไขตามความคิดเห็น


การพิถีพิถันเกี่ยวกับคำศัพท์มันเป็น "การสร้างอินสแตนซ์ชัดเจน"
Richard Corden

2

ลองมาตัวอย่างหนึ่งสมมติว่าด้วยเหตุผลบางอย่างที่คุณต้องการมีคลาสเทมเพลต:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

ถ้าคุณคอมไพล์รหัสนี้ด้วย Visual Studio - มันทำงานนอกกรอบ gcc จะสร้างข้อผิดพลาด linker (หากไฟล์ส่วนหัวเดียวกันถูกใช้จากไฟล์. cpp หลายไฟล์):

error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here

เป็นไปได้ที่จะย้ายการใช้งานไปยังไฟล์. cpp แต่คุณต้องประกาศคลาสเช่นนี้ -

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test();

template <>
void DemoT<bool>::test();

// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;

แล้ว. cpp จะมีลักษณะเช่นนี้:

//test_template.cpp:
#include "test_template.h"

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

หากไม่มีสองบรรทัดสุดท้ายในไฟล์ส่วนหัว - gcc จะทำงานได้ดี แต่ Visual Studio จะสร้างข้อผิดพลาด:

 error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function

ไวยากรณ์คลาสเทมเพลตเป็นตัวเลือกในกรณีที่คุณต้องการแสดงฟังก์ชั่นผ่านการส่งออก dll แต่สิ่งนี้ใช้ได้เฉพาะกับแพลตฟอร์ม windows เท่านั้นดังนั้น test_template.h อาจมีลักษณะเช่นนี้:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

#ifdef _WIN32
    #define DLL_EXPORT __declspec(dllexport) 
#else
    #define DLL_EXPORT
#endif

template <>
void DLL_EXPORT DemoT<int>::test();

template <>
void DLL_EXPORT DemoT<bool>::test();

ด้วยไฟล์. cpp จากตัวอย่างก่อนหน้า

อย่างไรก็ตามสิ่งนี้จะทำให้ปวดหัวกับ linker ได้มากกว่าดังนั้นจึงแนะนำให้ใช้ตัวอย่างก่อนหน้าถ้าคุณไม่ส่งออกฟังก์ชัน. dll


1

ได้เวลาอัพเดทแล้ว! สร้างไฟล์อินไลน์ (.inl หรือไฟล์อื่น ๆ ) และคัดลอกคำจำกัดความของคุณทั้งหมดในนั้น อย่าลืมเพิ่มเทมเพลตด้านบนแต่ละฟังก์ชัน ( template <typename T, ...>) ตอนนี้แทนที่จะรวมไฟล์ส่วนหัวในไฟล์อินไลน์ที่คุณทำตรงกันข้าม รวมไฟล์อินไลน์หลังจากการประกาศคลาสของคุณ ( #include "file.inl")

ฉันไม่รู้จริง ๆ ว่าทำไมไม่มีใครพูดถึงเรื่องนี้ ฉันไม่เห็นข้อเสียทันที


25
ข้อเสียทันทีคือพื้นฐานเหมือนกับการกำหนดฟังก์ชันเทมเพลตโดยตรงในส่วนหัว เมื่อคุณ#include "file.inl"ประมวลผลล่วงหน้าจะวางเนื้อหาของfile.inlโดยตรงในส่วนหัว ไม่ว่าด้วยเหตุผลใดก็ตามที่คุณต้องการหลีกเลี่ยงการนำไปใช้ในส่วนหัวโซลูชันนี้ไม่ได้แก้ปัญหานั้น
Cody Grey

5
- และหมายความว่าคุณไม่จำเป็นต้องมีภาระทางเทคนิคเพิ่มภาระให้ตัวเองด้วยงานเขียนทุกอย่างที่ต้องใช้ในการtemplateนิยามคำศัพท์นอกบรรทัด ฉันเข้าใจว่าทำไมผู้คนถึงต้องการทำเช่นนั้น - เพื่อให้เกิดความเท่าเทียมกันมากที่สุดด้วยการประกาศ / คำจำกัดความที่ไม่ใช่เทมเพลตเพื่อให้การประกาศอินเทอร์เฟซดูเป็นระเบียบ ฯลฯ - แต่ก็ไม่คุ้มกับความยุ่งยากเสมอไป เป็นกรณีของการประเมินการแลกเปลี่ยนระหว่างทั้งสองฝ่ายและเลือกสิ่งที่แย่ที่สุด ... จนกระทั่งnamespace classกลายเป็นเรื่อง: โอ [ โปรดเป็นสิ่งที่ ]
underscore_d

2
@ แอนดรูว์ดูเหมือนว่ามันจะติดอยู่ในท่อของคณะกรรมการถึงแม้ว่าฉันคิดว่าฉันเห็นคนพูดว่าไม่ได้ตั้งใจ ฉันหวังว่ามันจะทำให้มันกลายเป็น C ++ 17 อาจจะเป็นทศวรรษหน้า
underscore_d

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

0

ไม่มีอะไรผิดปกติกับตัวอย่างที่คุณให้ แต่ฉันต้องบอกว่าฉันเชื่อว่ามันไม่มีประสิทธิภาพในการจัดเก็บคำจำกัดความของฟังก์ชั่นในไฟล์ cpp ฉันเข้าใจเฉพาะความต้องการแยกการประกาศและคำจำกัดความของฟังก์ชัน

เมื่อใช้ร่วมกับการสร้างคลาสอินสแตนซ์ชัดแจ้ง Boost Concept Check Library (BCCL) สามารถช่วยคุณสร้างโค้ดฟังก์ชันเทมเพลตในไฟล์ cpp


8
มันไม่มีประสิทธิภาพอะไร
Cody Grey

0

ไม่มีข้อใดถูกใช้งานได้สำหรับฉันดังนั้นนี่คือวิธีที่ y แก้ไขชั้นเรียนของฉันมีวิธีการ 1 วิธีเท่านั้น ..

.h

class Model
{
    template <class T>
    void build(T* b, uint32_t number);
};

.cpp

#include "Model.h"
template <class T>
void Model::build(T* b, uint32_t number)
{
    //implementation
}

void TemporaryFunction()
{
    Model m;
    m.build<B1>(new B1(),1);
    m.build<B2>(new B2(), 1);
    m.build<B3>(new B3(), 1);
}

เป็นการหลีกเลี่ยงข้อผิดพลาดของ linker และไม่จำเป็นต้องเรียกใช้ TemporaryFunction เลย

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