ความแตกต่างของอินสแตนซ์ constexpr สองตัวของตัวชี้ __func__ ยังคงเป็น constexpr หรือไม่


14

นี่คือ C ++ ที่ถูกต้องหรือไม่

int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}

GCC และ MSVC คิดว่ามันตกลงดังกราวคิดว่ามันไม่ได้: คอมไพเลอร์ Explorer ที่


คอมไพเลอร์ทุกคนยอมรับว่าอันนี้ตกลง: คอมไพเลอร์ Explorer ที่

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}

เสียงดังกราวอีกครั้งไม่ชอบอันนี้ แต่อันอื่นก็โอเคกับมัน: Compiler Explorer

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = __func__;
    constexpr auto sz = p2 - p;
    return sz;
}

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

#include <functional>

int main() {
    constexpr std::equal_to<const char*> eq{};
    static_assert(eq(__func__, __func__));
}

เสียงดังกราวคิดeq(__func__, __func__)ไม่ได้แสดงออกอย่างต่อเนื่องแม้ว่าจะstd::equal_to::operator() เป็น constexpr คอมไพเลอร์อื่น ๆ ไม่บ่น: Compiler Explorer


เสียงดังกราวจะไม่รวบรวมอันนี้เช่นกัน บ่นว่า__func__ == __func__ไม่ใช่นิพจน์คงที่: Compiler Explorer

int main() {
    static_assert(__func__ == __func__);
}

จากFunction_definition , __func__เป็นถ้าstatic const char __func__[] = "function-name";และเทียบเท่าเป็นที่ยอมรับสาธิต ...
Jarod42

น่าสนใจมันใช้งานได้ถ้าคุณเริ่มต้นตัวแปร constexpr ด้วย__func__และใช้มันใน static_assert ...
florestan

@ Jarod42 ดังนั้นนี่คือข้อผิดพลาดในเสียงดังกราว?
Ayxan

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

1
ดูเพิ่มเติมCWG1962ซึ่งอาจลบออก__func__จากการประเมิน constexpr ทั้งหมด
Davis Herring

คำตอบ:


13

__func__ใน C ++ เป็นตัวระบุ โดยเฉพาะอย่างยิ่งมันอ้างอิงวัตถุที่เฉพาะเจาะจง จาก[dcl.fct.def.general] / 8 :

ตัวแปรที่กำหนดไว้ล่วงหน้าฟังก์ชั่นท้องถิ่น_­_­func_­_­มีการกำหนดราวกับว่าคำนิยามของรูปแบบ

static const char __func__[] = "function-name";

ได้รับการจัดเตรียมไว้ซึ่ง function-name เป็นสตริงที่กำหนดโดยการนำไปใช้งาน มันไม่ได้ระบุว่าตัวแปรดังกล่าวมีที่อยู่ที่แตกต่างจากที่ของวัตถุอื่น ๆ ในโปรแกรม

ในฐานะที่เป็นตัวแปรที่กำหนดไว้ล่วงหน้าฟังก์ชั่นท้องถิ่นคำจำกัดความนี้ (ราวกับว่า) ปรากฏขึ้นที่จุดเริ่มต้นของบล็อกฟังก์ชั่น ดังนั้นการใช้งาน__func__ภายในบล็อกนั้นจะอ้างอิงถึงตัวแปรนั้น

ในส่วนของ "วัตถุอื่น ๆ " ตัวแปรกำหนดวัตถุ __func__ชื่อวัตถุที่กำหนดโดยตัวแปรนั้น ดังนั้นภายในฟังก์ชั่นการใช้__func__ชื่อตัวแปรเดียวกันทั้งหมด สิ่งที่ไม่ได้นิยามคือตัวแปรนั้นเป็นวัตถุที่แตกต่างจากวัตถุอื่นหรือไม่

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

ตอนนี้กฎของ C ++ ราวกับว่าอนุญาตให้การใช้งานเบี่ยงเบนไปจากสิ่งนี้ แต่พวกมันไม่สามารถทำได้ในแบบที่ตรวจพบได้ ดังนั้นในขณะที่ตัวแปรนั้นอาจมีหรือไม่มีที่อยู่ที่แตกต่างจากวัตถุอื่น ๆ การใช้งาน__func__ในฟังก์ชั่นเดียวกันจะต้องทำตัวเหมือนว่าพวกเขากำลังอ้างถึงวัตถุเดียวกัน

เสียงดังกราวไม่ได้ใช้__func__วิธีนี้ ดูเหมือนว่าจะใช้มันราวกับว่ามันกลับสตริงตัวอักษร prvalue ของชื่อฟังก์ชั่น ตัวอักษรสตริงที่แตกต่างกันสองตัวไม่จำเป็นต้องอ้างถึงวัตถุเดียวกันดังนั้นการลบพอยน์เตอร์ของพวกเขาคือ UB และพฤติกรรมที่ไม่ได้กำหนดในบริบทการแสดงออกอย่างต่อเนื่องจะไม่ถูกต้อง

สิ่งเดียวที่ทำให้ฉันลังเลที่จะพูดว่าเสียงดังดังผิด 100% ที่นี่คือ[temp.arg.nontype] / 2 :

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

...

  • _­_­func_­_ตัวแปรที่กำหนดไว้ล่วงหน้า

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

ดังนั้นในบางระดับฉันจะบอกว่ามาตรฐานกำลังพูดออกมาจากทั้งสองด้านของปาก


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

สิ่งที่เกี่ยวกับ "มันไม่ได้ระบุว่าตัวแปรดังกล่าวมีที่อยู่ที่แตกต่างจากที่ของวัตถุอื่น ๆ ในโปรแกรม" ส่วนหนึ่ง? พฤติกรรมที่ไม่ระบุรายละเอียดหมายถึงการไม่กำหนดในพฤติกรรมของเครื่องจักรนามธรรม นั่นอาจเป็นปัญหาสำหรับการประเมิน constexpr หรือไม่? จะเกิดอะไรขึ้นถ้าการปรากฏครั้งแรกของ__func__ที่อยู่นั้นเหมือนกับของวัตถุอื่นและในการเกิดขึ้นครั้งที่สองของ__func__มันไม่ใช่? จริงอยู่ที่นั่นไม่ได้หมายความว่าที่อยู่แตกต่างกันระหว่างสองกรณี แต่ฉันยังสับสนอยู่!
Johannes Schaub - litb

@ JohannesSchaub-litb: " แล้วเกี่ยวกับ" มันไม่ได้ระบุว่าตัวแปรดังกล่าวมีที่อยู่ที่แตกต่างจากวัตถุอื่น ๆ ในโปรแกรม "ส่วนหนึ่ง " มันเกี่ยวกับอะไร __func__ไม่ใช่มาโคร มันเป็นตัวระบุซึ่งชื่อตัวแปรที่เฉพาะเจาะจงและดังนั้นจึงเป็นวัตถุที่เฉพาะเจาะจง ดังนั้นการใช้__func__ฟังก์ชั่นใด ๆในฟังก์ชั่นเดียวกันควรส่งผลให้เกิดค่า glvalue ที่อ้างถึงวัตถุเดียวกัน อย่างน้อยก็ไม่สามารถนำไปใช้ในทางที่เป็นไปไม่ได้
Nicol Bolas

@Nicol ที่อ้างถึงวัตถุเดียวกัน แต่วัตถุนั้นในทันทีอาจมีที่อยู่เดียวกับวัตถุอื่น และในทันทีอื่น ๆ ยังไม่ได้ ฉันไม่ได้บอกว่าเป็นปัญหา แต่ฉันแค่เตือนทุกคนถึงความเป็นไปได้นี้ และหลังจากนั้นฉันอาจจะเข้าใจผิดด้วยดังนั้นฉันจึงพูดเช่นนี้ด้วยความหวังว่าจะได้รับการแก้ไขหรือได้รับการยืนยัน
Johannes Schaub - litb

@ JohannesSchaub-litb: " แต่วัตถุนั้นในทันทีอาจมีที่อยู่เดียวกับวัตถุอื่น " นั่นไม่ได้รับอนุญาตภายใต้โมเดลวัตถุ C ++ วัตถุสองชิ้นไม่ถูกซ้อนอยู่ภายในวัตถุอื่นไม่สามารถอยู่ภายในช่วงเวลาเดียวกันในที่จัดเก็บข้อมูลเดียวกันในเวลาเดียวกัน และวัตถุที่สงสัยนั้นมีระยะเวลาการจัดเก็บแบบคงที่ดังนั้นหากคุณไม่ได้ใช้ตำแหน่งnewมันจะไม่ไปที่ใดจนกว่าโปรแกรมจะสิ้นสุด
Nicol Bolas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.