ถ้า constexpr ที่มี static_assert ในแลมบ์ดาคอมไพเลอร์ตัวไหนถูกต้อง?


13

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

โค้ดต่อไปนี้คอมไพล์ด้วย gcc แต่เสียงดังกราวกระตุ้นการยืนยันแม้ว่าif constexprจะไม่เป็นจริงก็ตาม

#include <utility>

template<typename T> constexpr std::false_type False;

template<typename T>
void foo() {

    auto f = [](auto x) {
        constexpr int val = decltype(x)::value;
        if constexpr(val < 0) {
            static_assert(False<T>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

int main() {
    foo<int>();
}

ตัวอย่างที่อาศัยอยู่ที่นี่

มันสามารถได้รับการแก้ไขโดยการแทนโดยFalse<T>False<decltype(x)>

ดังนั้นคำถามคือ: คอมไพเลอร์ใดถูก? ฉันคิดว่า gcc นั้นถูกต้องเพราะเงื่อนไขในstatic_assertนั้นขึ้นอยู่กับTแต่ฉันไม่แน่ใจ


นี้ถามคำถามประเภทเดียวกัน แต่มาจากทิศทางที่ตรงข้าม: stackoverflow.com/questions/59393908/...
NathanOliver

1
@mfnx ไม่สามารถทำซ้ำได้ คุณสามารถแบ่งปันตัวอย่างได้ไหม?
NathanOliver

2
ฉันจะบอกว่าทั้งสองถูก (เกิดขึ้นไม่ดี NDR): static_assert(False<int>, "AAA");เทียบเท่ากับstatic_assert(false, "AAA");ภายในแลมบ์ดา
Jarod42

2
@mfnx คุณเปลี่ยนค่าของค่าคงที่แล้ว การใช้ตัวอย่างของ OP โดยที่ค่าคงที่คือf(std::integral_constant<int, 1>{});Wandbox ไม่ทำให้เกิดการยืนยัน: wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver ใช่คุณถูกต้องขอโทษสำหรับเสียงดัง ดูเหมือนว่า gcc นั้นถูกต้องเนื่องจากโค้ดใน constexpr ควรถูกทิ้งหากค่าคงที่> = 0;
mfnx

คำตอบ:


1

จาก[stmt.if] / 2 (เน้นที่เหมือง)

ถ้าคำสั่ง if เป็นของรูปแบบถ้า constexpr ค่าของเงื่อนไขจะเป็นการแสดงออกคงที่แปลงบริบทของประเภทบูล; แบบฟอร์มนี้เรียกว่า constexpr if statement หากค่าของเงื่อนไขที่ถูกแปลงเป็นเท็จสถานะย่อยแรกคือคำสั่งที่ถูกทิ้งมิฉะนั้นสถานะย่อยที่สองถ้ามีจะเป็นคำสั่งที่ถูกทิ้ง ในระหว่างการสร้างอินสแตนซ์ของเอนทิตีที่ปิดล้อม ([temp.pre]) หากเงื่อนไขไม่ได้ขึ้นอยู่กับค่าหลังจากอินสแตนซ์ของมันสถานะย่อยที่ถูกทิ้ง (ถ้ามี) จะไม่ถูกสร้าง

การอ่านว่ามีใครคิดว่าการยืนยันแบบคงที่จะถูกทิ้ง แต่นี่ไม่ใช่กรณี

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

จาก[temp.res] / 8 (เน้นการทำเหมือง)

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

  • (8.1) ไม่สามารถสร้างความเชี่ยวชาญที่ถูกต้องสำหรับเทมเพลตหรือสถานะย่อยของ constexpr หากคำสั่งภายในเทมเพลตและเทมเพลตไม่ได้สร้างอินสแตนซ์หรือ

[ ... ]

ใช่แน่นอนFalse<T>ขึ้นอยู่กับTคุณ ปัญหาคือแลมบ์ดาทั่วไปเป็นเทมเพลตและFalse<T>ไม่ได้ขึ้นอยู่กับพารามิเตอร์เทมเพลตของแลมบ์ดา

สำหรับTที่False<T>เป็นเท็จการยืนยันแบบคงที่จะเป็นเท็จเสมอไม่ว่าอาร์กิวเมนต์แม่แบบใดจะถูกส่งไปยังแลมบ์ดา

คอมไพเลอร์สามารถเห็นได้ว่าสำหรับอินสแตนซ์ใด ๆ ของเทมเพลตoperator()การยืนยันแบบคงที่จะทริกเกอร์สำหรับ T ปัจจุบันดังนั้นข้อผิดพลาดของคอมไพเลอร์

ทางออกสำหรับสิ่งนี้จะขึ้นอยู่กับx:

template<typename T>
void foo() {

    auto f = [](auto x) {
        if constexpr(x < 0) {
            static_assert(False<decltype(x)>, "AAA");
        }
    };

    f(std::integral_constant<int, 1>{});
}

ตัวอย่างสด


13

กฎปกติที่นี่คือ[temp.res] / 8 :

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

เมื่อคุณยกตัวอย่างfoo<T>การstatic_assertที่คุณมีคือไม่ขึ้น มันจะกลายเป็นstatic_assert(false)- สำหรับ instantiations fเป็นไปได้ทั้งหมดของผู้ประกอบการเรียกร้องของแลมบ์ดาทั่วไป ไม่จำเป็นต้องมีการวินิจฉัย เสียงดังกราววินิจฉัย gcc ไม่ได้ ทั้งสองถูกต้อง

โปรดทราบว่าไม่สำคัญว่าที่static_assertนี่จะถูกทิ้ง

มันสามารถได้รับการแก้ไขโดยการแทนโดยFalse<T>False<decltype(x)>

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

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