ประเภทที่ไม่ระบุสามารถใช้ในนิพจน์ 'ต้องใช้' ของ C ++ 20 ได้อย่างไร

Aug 16 2020

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

ฉันพยายามเขียนสิ่งที่ชอบ:

template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

อย่างไรก็ตามทั้ง gcc และ clang ปฏิเสธสิ่งนี้ทำให้เกิดข้อผิดพลาดที่ไม่สามารถใช้ 'auto' ในรายการพารามิเตอร์ของนิพจน์ที่ต้องการด้วยวิธีนี้

อีกทางเลือกหนึ่งคือการใส่ประเภท 'x' เป็นพารามิเตอร์เทมเพลตที่สอง:

template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

แต่สิ่งนี้จำเป็นต้องระบุ TX อย่างชัดเจนเมื่อใดก็ตามที่ใช้แนวคิดนี้จะไม่สามารถอนุมานได้:

struct S { void Foo(int); };
static_assert(HasFooMethod<S>);         // doesn't compile
static_assert(HasFooMethod<S, int>);    // the 'int' must be specified

มีวิธีใดในการเขียนแนวคิดที่อนุญาตให้ Foo โต้แย้งประเภทที่ไม่ระบุ ?

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

คำตอบ

4 NicolBolas Aug 17 2020 at 13:48

แนวคิดไม่ได้มีจุดมุ่งหมายเพื่อจัดหาฟังก์ชันการทำงานที่คุณต้องการ ดังนั้นพวกเขาจึงไม่ให้มัน

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

ภายในเทมเพลตที่คุณ จำกัด ไว้มากถ้าคุณเขียนนิพจน์t.Foo(x)คุณก็จะรู้ประเภทของx. เป็นประเภทคอนกรีตพารามิเตอร์เทมเพลตหรือชื่อที่ได้มาจากพารามิเตอร์เทมเพลต ไม่ว่าจะด้วยวิธีใดประเภทของxจะมีอยู่ในเทมเพลตที่ถูก จำกัด

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

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

ความต้องการข้อ จำกัด ของคุณจะไม่ได้FooCallableWithHasFooMethod

NathanReed Aug 17 2020 at 16:30

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

struct anything
{
    // having both these conversions allows Foo's argument to be either
    // a value, an lvalue reference, or an rvalue reference

    template <typename T>
    operator T&();

    template <typename T>
    operator T&&();
};

โปรดทราบว่าไม่จำเป็นต้องใช้ตัวดำเนินการเหล่านี้เนื่องจากจะใช้ในบริบทที่ไม่ได้ประเมินเท่านั้น (และไม่สามารถนำไปใช้กับ T ทุกประเภทได้)

จากนั้นHasFooMethodสามารถเขียนเป็น:

template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
    { t.Foo(a) } -> std::same_as<void>;
};