std :: pair <auto, auto> return type


16

ผมเล่นรอบกับในauto std::pairในรหัสด้านล่างฟังก์ชั่นfควรจะส่งกลับstd::pairประเภทซึ่งขึ้นอยู่กับพารามิเตอร์แม่แบบ

ตัวอย่างการทำงาน:

ตัวอย่าง 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

ใช้งานได้กับ gcc 9.2, gcc 10.0, clang 9.0 และ clang 10.0

ถัดไปฉันต้องการเขียนประเภทส่งคืนอย่างstd::pairชัดเจนเพื่อเหตุผลที่ชัดเจน:

ตัวอย่าง 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

ทั้ง gcc 9.2 / 10.0 และเสียงดังกราว 9.0 / 10.0 ล้มเหลวในการรวบรวม

gcc 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

จากข้อผิดพลาดที่ผ่านมา GCC 9.2 ดูเหมือนจะเชื่อว่าเป็นstd::pair<auto, auto> intสิ่งนี้จะอธิบายได้อย่างไร?

gcc 10.0

error: returning initializer list

ข้อผิดพลาดนี้เป็นที่เข้าใจได้ แต่ฉันคาดว่าตัวสร้างของstd::pairจะถูกเรียกหรือมีบางสิ่งที่ฉันหายไปที่นี่?

เสียงดังกราว 9.0 และ 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

โอเคเสียงดังกราวไม่ชอบสิ่งนี้ intจากข้อผิดพลาดที่สองมันก็ดูเหมือนว่าเสียงดังกราวยังเชื่อชนิดกลับเป็น

สุดท้ายเพื่อแก้ไขข้อผิดพลาดที่ได้รับจากการรวบรวมด้วย gcc 10.0 ฉันตัดสินใจกลับมาstd::pairอย่างชัดเจน:

ตัวอย่าง 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

เสียงดังกราว 9.0 และ 10.0

เหมือนเมื่อก่อน แต่มีเพิ่มเติม:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

เสียงดังกราวยังคงคิดว่าเราจะกลับมาint?

gcc 9.2

เหมือนเมื่อก่อน.

gcc 10.0

มันได้ผล!

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

คำตอบ:


23

ไวยากรณ์:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

เป็นส่วนหนึ่งของแนวคิด TS ดั้งเดิม แต่ไม่รวมอยู่ในข้อเสนอแนวคิดที่เป็นส่วนหนึ่งของ C ++ 20 ดังนั้นประเภทตัวยึดตำแหน่งเดียวใน C ++ 20 คือauto(และรูปแบบที่คล้ายกันauto**) decltype(auto)และตัวยึดตำแหน่งที่ จำกัด ( Concept autoและรูปแบบที่เปลี่ยนแปลง) ประเภทตัวยึดตำแหน่งซ้อนกันประเภทนี้จะมีประโยชน์มาก แต่ไม่ได้เป็นส่วนหนึ่งของ C ++ 20 ดังนั้นการประกาศฟังก์ชันนั้นจึงไม่ได้เกิดขึ้น

ตอนนี้ gcc อนุญาตเพราะ gcc ใช้ Concepts TS และฉันคิดว่าพวกเขาตัดสินใจที่จะเก็บคุณสมบัตินี้ไว้ เสียงดังกราวไม่เคยนำ TS มาใช้ดังนั้นจึงไม่เป็นเช่นนั้น

ทั้งสองวิธีนี้:

std::pair<auto, auto> f() { return {1, 2}; }

จะมีรูปแบบไม่ดีเสมอ ความหมายของไวยากรณ์คือการที่เราอนุมานชนิดการส่งคืนแล้วต้องใช้ให้ตรงกับpair<T, U>ในบางชนิดและT Uโดยทั่วไปเรากำลังพยายามเรียกใช้ฟังก์ชันที่คิดค้นขึ้น:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

แต่คุณไม่สามารถอนุมานประเภทได้{1, 2}- รายการ braced-init-list ไม่มีประเภท บางทีนี่อาจเป็นสิ่งที่ควรสำรวจ (อย่างน้อยก็เข้าใจง่ายในกรณีง่าย ๆ เช่นนี้) แต่ไม่เคยได้รับอนุญาต ดังนั้นการปฏิเสธมันถูกต้องทั้งสองวิธี

สุดท้าย:

GCC 9.2 ดูเหมือนจะเชื่อว่าเป็นstd::pair<auto, auto> intสิ่งนี้จะอธิบายได้อย่างไร?

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


รายการ "braced-init-list ไม่มีอาร์กิวเมนต์ชนิด" ไม่ชัดเจนสำหรับฉัน std :: pair <int, int> f () {return {1,2}; } ทำงานได้และ {1,2} ไม่มีประเภท (เรียกใช้ตัวสร้างของ std :: pair <int, int> ตามที่ฉันเข้าใจ) อาจจะด้วย <auto, auto> คอมไพเลอร์ไม่สามารถอนุมานชนิดของ 1 และ 2 ในรายการ initializer {1, 2}?
mfnx

@mfnx ไม่มีอาร์กิวเมนต์ประเภทแต่ไม่มีประเภท รายการ init ที่ได้รับการสนับสนุนสามารถใช้ได้ในบางสถานการณ์เท่านั้น - เช่นการเริ่มต้นประเภทที่รู้จัก แต่พวกเขาไม่สามารถใช้ในการหัก - เพราะพวกเขาไม่มีประเภท ยกเว้นauto x = {1, 2};งาน แต่เฉพาะในทุกประเภทเท่ากัน
Barry

2
คอมไพเลอร์ส่วนใหญ่แทนที่จะหยุดที่ข้อผิดพลาดแรกพยายามกู้คืนจากมันเพื่อให้พวกเขาสามารถรายงานข้อผิดพลาดเพิ่มเติม intนี้โดยทั่วไปหมายถึงสมมติว่าทุกอย่างเป็น unparseable ไม่ใช่ว่าintเป็นตัวยึดตำแหน่งในข้อความแสดงข้อผิดพลาด intคอมไพเลอร์จริงๆคิดว่ามันเป็น (เพื่อให้ชัดเจนยิ่งขึ้น gcc น่าจะได้กล่าวว่า "สมมติว่าเป็น int" ในบางจุด)
Raymond Chen

2
โปรดทราบว่าอีกช่องทางที่เป็นไปได้สำหรับการขยายจะอนุญาตให้ใช้การลดอาร์กิวเมนต์เทมเพลตคลาสสำหรับประเภทส่งคืนเนื่องจากstd::pair __f{1,2};ใช้ได้
Davis Herring

2
@DavisHerring ฉันไม่อยากมีstd::optional f() { return 4; }งานทำจริงๆ
Barry
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.