เทมเพลตคำหลัก 'typename' และ 'class' ต่างกันหรือไม่?


504

สำหรับเทมเพลตฉันเห็นการประกาศทั้งสอง:

template < typename T >
template < class T >

ความแตกต่างคืออะไร?

และคำหลักเหล่านั้นหมายถึงอะไรในตัวอย่างต่อไปนี้ (นำมาจากบทความ Wikipedia เกี่ยวกับเทมเพลตในภาษาเยอรมัน)

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};

คำตอบ:


430

typenameและclassสามารถใช้แทนกันได้ในกรณีพื้นฐานของการระบุแม่แบบ:

template<class T>
class Foo
{
};

และ

template<typename T>
class Foo
{
};

เทียบเท่า

ต้องบอกว่ามีเฉพาะกรณีที่มีความแตกต่างระหว่างและtypenameclass

คนแรกคือในกรณีของประเภทขึ้นอยู่กับ typenameใช้เพื่อประกาศเมื่อคุณอ้างอิงชนิดซ้อนที่ขึ้นอยู่กับพารามิเตอร์เทมเพลตอื่นเช่นtypedefในตัวอย่างนี้:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

คำถามที่สองที่คุณแสดงในคำถามของคุณถึงแม้ว่าคุณอาจไม่ได้ตระหนักถึง:

template < template < typename, typename > class Container, typename Type >

เมื่อระบุแม่แบบแม่แบบที่classคำหลักที่จะต้องนำมาใช้เป็นเหนือ - มันเป็นไม่ได้กันกับtypenameในกรณีนี้(หมายเหตุ: ตั้งแต่ C ++ 17 คำหลักที่ทั้งสองจะได้รับอนุญาตในกรณีนี้)

คุณต้องใช้classเมื่อสร้างอินสแตนซ์ของเท็มเพลตอย่างชัดเจน:

template class Foo<int>;

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


45
อันสุดท้ายนั้นค่อนข้างเป็นกรณีพิเศษที่คุณต้องใช้ class หรือ struct ไม่ใช่ typename เพื่อกำหนด class เห็นได้ชัดว่าทั้งสองบิตแรกของโค้ดไม่สามารถถูกแทนที่ด้วยtemplate <typename T> typename Foo {};เพราะ Foo <T> เป็นคลาสที่แน่นอนที่สุด
Steve Jessop

2
std::vector<int>::value_typeไม่ได้เป็นประเภทที่ขึ้นอยู่กับคุณไม่จำเป็นต้องtypenameมี - คุณต้องการมันถ้าประเภทขึ้นอยู่กับพารามิเตอร์แม่แบบพูดtemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche

2
และอีกครั้งparam_tไม่ใช่ประเภทตาม ประเภทการพึ่งพาเป็นชื่อที่ขึ้นอยู่กับพารามิเตอร์แม่แบบเช่นfoo<param_t>::some_typeไม่ใช่พารามิเตอร์แม่แบบ
Georg Fritzsche

2
c ++ 1Z ข้อเสนอN4051จะช่วยให้คุณใช้คือtypename template <typename> typename C
user4112979

4
ในฐานะของGCC 5, G ++ ขณะนี้ช่วยให้ typename ในพารามิเตอร์แม่แบบแม่แบบ
Chnossos

95

สำหรับการตั้งชื่อพารามิเตอร์เทมเพลตtypenameและclassเทียบเท่า §14.1.2:

ไม่มีความแตกต่างทาง semantic ระหว่าง class และ typename ใน template-parameter

typenameอย่างไรก็ตามเป็นไปได้ในบริบทอื่นเมื่อใช้เทมเพลต - เพื่อบอกใบ้เกี่ยวกับคอมไพเลอร์ที่คุณอ้างถึงชนิดที่ต้องพึ่งพา §14.6.2:

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

ตัวอย่าง:

typename some_template<T>::some_type

หากไม่มีtypenameคอมไพเลอร์ไม่สามารถบอกได้โดยทั่วไปว่าคุณกำลังอ้างถึงประเภทหรือไม่


2
ฉันเข้าใจกฎ แต่สิ่งที่ป้องกันไม่ให้คอมไพเลอร์ปฏิบัติต่อ some_template <T> เป็นประเภทภายใน ขออภัยหากฉันขาดอะไรที่ชัดเจน
batbrat

23

ในขณะที่ไม่มีความแตกต่างทางเทคนิคฉันได้เห็นทั้งสองเคยแสดงถึงสิ่งที่แตกต่างกันเล็กน้อย

สำหรับเทมเพลตที่ควรยอมรับประเภทใดก็ได้เช่น T รวมถึงบิวด์อิน (เช่นอาร์เรย์)

template<typename T>
class Foo { ... }

สำหรับเทมเพลตที่จะทำงานเฉพาะที่ T เป็นคลาสจริง

template<class T>
class Foo { ... }

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


15
ฉันไม่โทษคุณที่พูดถึงมัน แต่ฉันคิดว่านโยบายนี้ค่อนข้างเข้าใจผิดเนื่องจากโปรแกรมเมอร์ต้องใช้เวลาคิดเกี่ยวกับสิ่งที่ไม่สำคัญ ("ฉันใช้สิ่งที่ถูกต้องหรือไม่") เพื่อระบุสิ่งที่ไม่ถูกต้อง ' ไม่สำคัญ ("มีชนิดในตัวซึ่งใช้อินเทอร์เฟซที่ต้องการของพารามิเตอร์เทมเพลตนี้หรือไม่") หากมีการใช้สมาชิกของพารามิเตอร์เทมเพลต ( T t; int i = t.toInt();) คุณจำเป็นต้องมี "คลาสจริง" และรหัสของคุณจะไม่ถูกรวบรวมหากคุณจัดหาintให้T...
Steve Jessop

1
หากคุณต้องการ จำกัด การใช้งานในชั้นเรียนจริงคุณควรเพิ่มความเชี่ยวชาญในการโยน / ทำให้เกิดข้อผิดพลาดสำหรับประเภทที่ไม่ใช่ชั้นเรียน หากคุณต้องการ จำกัด การใช้งานเฉพาะบางคลาส ไม่ว่าในกรณีใดความแตกต่างของสไตลิสต์นั้นบอบบางเกินกว่าที่จะรับข้อความ
Potatoswatter

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

+1 ฉันทำด้วยตัวเองในบางครั้ง ... classหมายความว่าคุณไม่เพียงแค่คาดหวังว่า "คุณค่า" อาจจะสนับสนุนผู้ประกอบการบางคนคัดลอกหรือย้ายสิ่งปลูกสร้างและ / หรือการมอบหมาย แต่ต้องการประเภทที่สนับสนุนความหมายของสมาชิก การประกาศอย่างรวดเร็วที่สุดนั้นจะตั้งค่าความคาดหวังและความท้อแท้เช่นการจัดหาclassพารามิเตอร์แบบบิวท์อินในตัวเมื่อมันจะเป็นข้อผิดพลาดอย่างแน่นอน
Tony Delroy

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

7
  1. ไม่แตกต่าง
  2. พารามิเตอร์ประเภทContainerเทมเพลตเป็นเทมเพลตที่มีพารามิเตอร์ประเภทสองตัว

3
โดยทั่วไปมีความแตกต่าง
Hassan Syed

พารามิเตอร์ทั้งสองนั้นคอนเทนเนอร์นั้นมีเทมเพลทด้วยหรือไม่ ในตัวอย่างพวกเขาไม่มีชื่อใด ๆ และ - ในตัวอย่างนี้มันถูกเขียนว่า 'คลาสคอนเทนเนอร์' - อาจมี 'ชื่อคอนเทนเนอร์' แทนได้ไหม
Mat

2
@Mat: ใช่คำในการค้นหาเป็นพารามิเตอร์แม่แบบแม่แบบ / ข้อโต้แย้ง เช่น:template<template<class U> class V> struct C {};
Georg Fritzsche

6

ตัวอย่างข้อมูลนี้มาจากหนังสือเรียนรู้เบื้องต้นของ c ++ แม้ว่าฉันจะแน่ใจว่านี่เป็นสิ่งที่ผิด

พารามิเตอร์แต่ละประเภทจะต้องนำหน้าด้วยคลาสคำหลักหรือชื่อพิมพ์:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

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

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

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

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