พยายามทำความเข้าใจแม่แบบและค้นหาชื่อ


9

ฉันพยายามเข้าใจตัวอย่างโค้ดต่อไปนี้

ตัวอย่าง # 1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

ไม่ว่าจะเป็น gcc9 หรือ clang9 ข้อผิดพลาดที่นี่

ถามทำไมรหัสนี้ถึงคอมไพล์? เราไม่ได้ยกตัวอย่างA<B>เมื่อได้รับมรดกจาก B? ไม่มี VD ใน B ดังนั้นคอมไพเลอร์ไม่ควรโยนข้อผิดพลาดที่นี่หรือ

ตัวอย่าง # 2

template <typename T>
struct A
{
    static constexpr auto AB = T::AD; // <- No member named AD in B
};

struct B : A<B>
{
    static constexpr auto AD = 0xD;
};

ในกรณีนี้ gcc9 คอมไพล์ได้ดี แต่ clang9 ส่งข้อผิดพลาดว่า "ไม่มีสมาชิกชื่อ AD ใน B"

ถามทำไมมันคอมไพล์ด้วย gcc9 / ทำไมมันไม่คอมไพล์ด้วย clang9?

ตัวอย่าง # 3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

ที่นี่ทั้ง clang9 และ gcc9 มีข้อผิดพลาด gcc9 พูดว่า "การใช้ประเภทที่ไม่สมบูรณ์ 'struct B' ไม่ถูกต้อง"

ถาม: ถ้า struct B ไม่สมบูรณ์ที่นี่แล้วทำไมมันไม่สมบูรณ์ในตัวอย่างที่ 2?

ธงคอมไพเลอร์ที่ใช้: -std=c++17 -O3 -Wall -Werror. ขอบคุณล่วงหน้า!!!


@xception ไม่ได้ยกstruct Bตัวอย่างAกับBใช่หรือไม่
ผลข้างเคียงที่เปลี่ยนแปลงได้

clang9 โยนข้อผิดพลาดว่า "ไม่มีสมาชิกชื่อ AD ใน B" ตามที่Bไม่สมบูรณ์ ... แต่ไม่แน่ใจว่าสมาชิกควรจะสร้างอินสแตนท์เมื่อใด ..
23919

@MobileSideEffect โอ้ใช่ฉันไม่ดีอ่านว่าเป็นเทมเพลตเช่นกัน :(
xception

@ Jarod42 ทำไม gcc จึงคอมไพล์ดี
ผลข้างเคียงที่เปลี่ยนแปลงได้

1
ฉันตั้งค่าสถานะคำถามนี้ว่า "ต้องการโฟกัสมากกว่านี้" และคำถามมีคำถามมากกว่าหนึ่งข้อ (ด้วยเหตุนี้ฉันจึงสรุป) ดังนั้นทำไมการตั้งค่าสถานะของฉันจึงผิด
Dominique

คำตอบ:


4

ฉันเชื่อว่าสิ่งเหล่านี้เดือดลงไปที่[temp.inst] / 2 (เน้นที่เหมือง):

การสร้างอินสแตนซ์โดยนัยของความเชี่ยวชาญเทมเพลตคลาสทำให้การอินสแตนซ์โดยนัยของการประกาศ แต่ไม่ใช่ของคำจำกัดความอาร์กิวเมนต์เริ่มต้นหรือnoexcept-specifiers ของฟังก์ชันสมาชิกคลาสคลาสสมาชิกการกำหนดสมาชิกสมาชิกที่กำหนดขอบเขตสมาชิกข้อมูลแบบคงที่สมาชิกแม่แบบและ เพื่อน [ ... ]

และ[temp.inst] / 9

การดำเนินการจะไม่ยกตัวอย่างโดยนัย […] สมาชิกข้อมูลคงที่ของแม่แบบคลาส […] เว้นแต่ว่าจำเป็นต้องมีการสร้างอินสแตนซ์

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

ตัวอย่าง # 1

ถามทำไมรหัสนี้ถึงคอมไพล์? เราไม่ได้ยกตัวอย่างก. เมื่อสืบทอดจาก B หรือไม่? ไม่มี VD ใน B ดังนั้นคอมไพเลอร์ไม่ควรโยนข้อผิดพลาดที่นี่หรือ

A<B>คุณกำลังอินสแตนซ์ แต่การทำให้อินสแตนซ์เป็นA<B>เพียงการทำให้การประกาศเป็นไปอย่างรวดเร็วไม่ใช่คำจำกัดความของสมาชิกข้อมูลแบบคงที่ VBไม่เคยถูกใช้ในลักษณะที่จะต้องมีคำนิยามที่มีอยู่ คอมไพเลอร์ควรยอมรับรหัสนี้

ตัวอย่าง # 2

ถามทำไมมันคอมไพล์ด้วย gcc9 / ทำไมมันไม่คอมไพล์ด้วย clang9?

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

ตัวอย่าง # 3

ถาม: ถ้า struct B ไม่สมบูรณ์ที่นี่แล้วทำไมมันไม่สมบูรณ์ในตัวอย่างที่ 2?

ประเภทระดับเป็นเพียงที่สมบูรณ์ที่จุดที่คุณเข้าถึงปิด}ของชั้นระบุ [class.mem] / 6 ดังนั้นจึงBไม่สมบูรณ์ในระหว่างการสร้างอินสแตนซ์โดยนัยของตัวอย่างA<B>ทั้งหมดของคุณ เป็นเพียงแค่สิ่งนี้ไม่เกี่ยวข้องกับ Snippet # 1 ในตัวอย่าง # 2, เสียงดังกราวไม่ให้ข้อผิดพลาดNo member named AD in Bเป็นผล เช่นเดียวกับกรณีของ Snippet # 2 ฉันไม่สามารถหาถ้อยคำได้ว่าเมื่อใดการประกาศนามแฝงของสมาชิกจะเกิดขึ้นทันที อย่างไรก็ตามไม่เหมือนกับคำจำกัดความของสมาชิกข้อมูลแบบคงที่ไม่มีถ้อยคำในสถานที่ที่จะป้องกันการสร้างอินสแตนซ์ของการประกาศนามแฝงของสมาชิกอย่างชัดเจนในระหว่างการสร้างอินสแตนซ์ของแม่แบบคลาสโดยนัย ดังนั้นฉันจะบอกว่าพฤติกรรมของทั้ง GCC และเสียงดังกราวเป็นการตีความที่ถูกต้องของมาตรฐานในกรณีนี้ ...


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

ฉันดูข้อมูลจำเพาะและดูเหมือนว่ามีความแตกต่างระหว่าง C ++ 14 และ C ++ 17 ที่นี่ ใน C ++ 14 constexprสมาชิกข้อมูลแบบสแตติกเป็นเพียงการประกาศ C ++ 17 ได้รับinlineตัวแปรและconstexprบอกเป็นนัยinlineและสิ่งนี้ทำให้สมาชิกข้อมูลสแตติกในร่างกายประกาศคำจำกัดความ
Johannes Schaub - litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2กล่าวว่า "ประเภทของตัวแปรที่ประกาศโดยใช้ประเภทตัวแทนจะถูกหักออกจาก initializer การใช้งานนี้ได้รับอนุญาตในการเริ่มต้นการประกาศ ([dcl init]) ของตัวแปร ". ดังนั้นฉันจะยืนยันว่าคำจำกัดความของสมาชิกข้อมูลคงที่เป็นสิ่งจำเป็นเพราะมันมีการเริ่มต้น
Johannes Schaub - litb

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

หากไม่มีสิ่งใดที่จำเป็นต้องมีการกำหนดความหมายจะไม่ถูกสร้างอินสแตนซ์ แต่ในautoกรณีที่กฎบอกว่าการประกาศจะต้องเริ่มต้นการประกาศ นี่อาจเป็นกรณีที่ทราบว่าการประกาศเป็นคำจำกัดความ (เท่าที่ฉันรู้ .. ฉันอยู่นอกทนายไปสักพักแล้ว) ในอดีตมีกรณีที่คล้ายกันและeel.is/c++draft/temp.inst#2.sentence-3ถูกเพิ่มเข้ามาโดยมีการประกาศว่าคำจำกัดความนั้นมีการสร้างอินสแตนซ์เป็น "เป็นที่รู้จักกันว่าเป็นคำจำกัดความ" โดยแท้จริงแล้ว การสร้างนิยามให้เป็นอินสแตนซ์
Johannes Schaub - litb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.