C ++ - เหตุใดจึงต้องใช้คำหลัก 'เทมเพลต' ที่นี่


9

ฉันมีรหัสต่อไปนี้:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

เมื่อมีการสร้างนี้มีทั้ง GCC 9.2 และเสียงดังกราว (9.0) ฉันได้รับการรวบรวมข้อผิดพลาดเนื่องจากการคำหลักที่ถูกต้องสำหรับการกล่าวอ้างtemplate funเสียงดังกราวแสดง:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

ฉันไม่เข้าใจว่าทำไมคอมไพเลอร์คิดว่าfunเป็นชื่อที่ต้องพึ่งพาในบริบทของfเนื่องจากfไม่ได้เป็นแม่แบบเอง ถ้าฉันเปลี่ยนCเป็นชั้นเรียนปกติแทนแม่แบบข้อผิดพลาดจะหายไป; แต่ผมไม่เห็นเหตุผลที่ควรจะมีข้อผิดพลาดในครั้งแรกตั้งแต่ค่าSมิได้ขึ้นอยู่กับfTC

ผิดปกติพอสมควร MSVC 19.22 รวบรวมสิ่งนี้ได้ดี


บันทึก

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


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Bhargav Rao

คำตอบ:


10

พิจารณา :

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i)จะแยกเป็นแสดงออกที่มีการดำเนินงานเปรียบเทียบสอง<และ>และนี่คือที่ดีสำหรับ # 1 แต่ล้มเหลวสำหรับ # 2

หากสิ่งนี้ถูกเปลี่ยนเป็นs.template a<0>(i)# 2 ก็โอเคและ # 1 ล้มเหลว ดังนั้นtemplateคำหลักจะไม่ซ้ำซ้อนที่นี่

MSVC สามารถตีความนิพจน์s.a<0>(i)ทั้งสองวิธีภายในโปรแกรมเดียวกัน แต่สิ่งนี้ไม่ถูกต้องตามมาตรฐาน แต่ละนิพจน์ควรมีการแยกวิเคราะห์เพียงครั้งเดียวสำหรับคอมไพเลอร์ที่จะจัดการ


ฉันยังไม่เข้าใจในสิ่งนี้ ตัวอย่างของคุณแสดงให้เห็นว่าในกรณีนี้คุณใช้ความเชี่ยวชาญพิเศษCหรืออื่น ๆ แต่คุณไม่สามารถสร้างอินสแตนซ์ทั้งสองได้ templateคำหลักที่นี่ยังคงเป็น IMHO ไม่จำเป็นเพราะที่Sได้รับเลือกขึ้นอยู่กับที่Cคุณยกตัวอย่าง หากไม่มีtemplateคำหลักคุณจะสามารถยกตัวอย่างทั้งสองอย่างและพฤติกรรมของ f จะแตกต่างกันไปในแต่ละอินสแตนซ์
Martin

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

นั่นทำให้รู้สึก ขอบคุณ!
Martin

7

funอาจจะหรืออาจจะไม่เป็นฟังก์ชั่นแม่แบบ (หรืออาจจะไม่ได้อยู่ที่ทั้งหมด) class Cทั้งนี้ขึ้นอยู่กับพารามิเตอร์ของแม่แบบ

นั่นเป็นเพราะคุณสามารถมีความเชี่ยวชาญS(ไม่มีความเชี่ยวชาญC):

template <> struct C<int>::S {};

เนื่องจากคอมไพเลอร์ต้องการทราบว่าfunเป็นเทมเพลตหรือไม่เมื่อดูครั้งแรกclass C(ก่อนที่จะแทนที่พารามิเตอร์เทมเพลต) templateจำเป็นต้องมี


1
ใจ ...
ปลื้ม

การติดตามนี้ก็คือว่าสิ่งเหล่านี้นิยามใหม่ของความสามารถที่เคยเข้าถึงได้โดยS fหากพวกเขาทำไม่ได้การมีข้อ จำกัด นี้ก็ไม่สมเหตุสมผลเพราะfจะไม่สามารถเห็นพวกเขาอยู่ดี
Martin

@ มาร์ตินทั้งGCC, Clangและ MSVC fหาได้แล้ว
HolyBlackCat

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