ข้อดีของการใช้การอ้างอิงการส่งต่อแบบอิงตามช่วงสำหรับลูปคืออะไร?


115

const auto&จะเพียงพอถ้าฉันต้องการดำเนินการอ่านอย่างเดียว อย่างไรก็ตามฉันได้ชน

for (auto&& e : v)  // v is non-const

สองสามครั้งเมื่อเร็ว ๆ นี้ สิ่งนี้ทำให้ฉันสงสัย:

เป็นไปได้ไหมว่าในบางกรณีมุมที่คลุมเครือมีประโยชน์ด้านประสิทธิภาพในการใช้การอ้างอิงการส่งต่อเมื่อเทียบกับauto&หรือconst auto&?

( shared_ptrเป็นผู้ต้องสงสัยคดีมุมอับ)


อัปเดต สองตัวอย่างที่ฉันพบในรายการโปรดของฉัน:

ข้อเสียของการใช้การอ้างอิง const เมื่อทำซ้ำในประเภทพื้นฐานหรือไม่?
ฉันสามารถทำซ้ำค่าของแผนที่โดยใช้ range-based for loop ได้หรือไม่?

โปรดตั้งสมาธิกับคำถาม: เหตุใดฉันจึงต้องการใช้ auto && แบบอิงตามช่วงสำหรับลูป


4
คุณไม่ได้จริงๆเห็นมัน "มักจะ"?
Lightness Races ใน Orbit

2
ฉันไม่แน่ใจว่ามีบริบทเพียงพอในคำถามของคุณที่จะวัดได้ว่า "บ้า" ตรงไหนที่คุณเห็น
Lightness Races ใน Orbit

2
@LightnessRacesinOrbit เรื่องสั้นเรื่องสั้น: ทำไมฉันถึงต้องการใช้auto&&ในช่วงสำหรับลูป?
Ali

คำตอบ:


94

ข้อดีอย่างเดียวที่ฉันเห็นคือเมื่อตัววนซ้ำลำดับส่งคืนการอ้างอิงพร็อกซีและคุณต้องดำเนินการกับการอ้างอิงนั้นในลักษณะที่ไม่ใช่ const ตัวอย่างเช่นพิจารณา:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto& e : v)
        e = true;
}

สิ่งนี้ไม่ได้รวบรวมเนื่องจาก rvalue vector<bool>::referenceส่งคืนจากiteratorจะไม่ผูกกับการอ้างอิง lvalue ที่ไม่ใช่ const แต่จะได้ผล:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto&& e : v)
        e = true;
}

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

#include <vector>

int main()
{
    std::vector<bool> v(10);
    // using auto&& so that I can handle the rvalue reference
    //   returned for the vector<bool> case
    for (auto&& e : v)
        e = true;
}

แก้ไข

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


3
ในทางกลับกันไม่มีข้อเสียที่ชัดเจนใช่หรือไม่? (นอกเหนือจากคนที่อาจสร้างความสับสนซึ่งฉันไม่คิดว่าจะมีค่าพูดถึงมากนัก)
ildjarn

7
อีกคำหนึ่งสำหรับการเขียนโค้ดที่ทำให้ผู้คนสับสนโดยไม่จำเป็นคือการเขียนโค้ดที่สับสน ที่ดีที่สุดคือทำให้โค้ดของคุณเรียบง่ายที่สุด แต่ไม่ง่ายไปกว่านี้ วิธีนี้จะช่วยให้บั๊กนับถอยหลัง ที่ถูกกล่าวว่าเมื่อ && คุ้นเคยกันมากขึ้นอาจจะ 5 ปีนับจากนี้ผู้คนจะคาดหวังว่าสำนวน && อัตโนมัติ (สมมติว่ามันไม่เป็นอันตราย) ฉันไม่รู้ว่าจะเกิดขึ้นหรือไม่ แต่ความเรียบง่ายอยู่ในสายตาของผู้มองและถ้าคุณเขียนมากกว่าแค่ตัวคุณเองให้คำนึงถึงผู้อ่านของคุณด้วย
Howard Hinnant

7
ฉันชอบconst auto&เมื่อต้องการให้คอมไพเลอร์ช่วยตรวจสอบว่าฉันไม่ได้แก้ไของค์ประกอบในลำดับโดยไม่ได้ตั้งใจ
Howard Hinnant

31
โดยส่วนตัวแล้วฉันชอบใช้auto&&ในโค้ดทั่วไปที่ฉันต้องแก้ไของค์ประกอบของลำดับ auto const&ถ้าผมทำไม่ได้ผมก็จะติด
Xeo

7
@Xeo: +1 เป็นเพราะผู้ที่ชื่นชอบตัวคุณเองทดลองและผลักดันวิธีการทำสิ่งต่างๆที่ดีขึ้นอยู่ตลอดเวลา C ++ จึงยังคงพัฒนาอย่างต่อเนื่อง ขอบคุณ. :-)
Howard Hinnant

26

การใช้auto&&หรือการอ้างอิงแบบสากลกับfor-loop ตามช่วงมีข้อดีที่คุณจะจับสิ่งที่คุณได้รับ สำหรับชนิดมากที่สุดของ iterators คุณอาจจะได้รับอย่างใดอย่างหนึ่งT&หรือสำหรับบางชนิดT const& Tกรณีที่น่าสนใจคือการอ้างอิงตัววนซ้ำจะให้ผลชั่วคราว: C ++ 2011 มีข้อกำหนดที่ผ่อนคลายและตัวทำซ้ำไม่จำเป็นต้องให้ค่า lvalue การใช้การอ้างอิงสากลตรงกับการส่งต่ออาร์กิวเมนต์ในstd::for_each():

template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
    for (; it != end; ++it) {
        f(*it); // <---------------------- here
    }
    return f;
}

วัตถุฟังก์ชั่นfสามารถรักษาT&, T const&และTแตกต่างกัน เหตุใดเนื้อความของลูปตามช่วงจึงควรforแตกต่างกัน แน่นอนในการใช้ประโยชน์จากการอนุมานประเภทโดยใช้การอ้างอิงสากลคุณต้องส่งต่อไปตามลำดับ:

for (auto&& x: range) {
    f(std::forward<decltype(x)>(x));
}

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


9

auto&&ฉันแทบมักจะใช้ ทำไมถึงโดนกัดโดย edge case ในเมื่อไม่ต้องทำ? พิมพ์สั้นกว่าด้วยและฉันก็พบว่ามันมากกว่า ... โปร่งใส เมื่อคุณใช้auto&& xแล้วคุณจะรู้ว่าที่xแน่นอน*itทุกครั้ง


29
ปัญหาของฉันคือการที่คุณจะให้ขึ้นconst-ness ด้วยauto&&ถ้าconst auto&พอเพียง คำถามถามถึงกรณีมุมที่ฉันสามารถถูกกัดได้ ไดเอทมาร์หรือฮาวเวิร์ดยังไม่ได้กล่าวถึงกรณีใดบ้าง
อาลี

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

@cyberbisson ฉันเพิ่งเจอสิ่งนี้: ฉันจะบังคับใช้การอ้างอิง rvalue โดยไม่ระบุประเภทอย่างชัดเจนได้อย่างไร บางอย่างเช่นfor (std::decay_t<decltype(*v.begin())>&& e: v)? ฉันเดาว่ามีวิธีที่ดีกว่านี้ ...
Jerry Ma

@JerryMa มันขึ้นอยู่กับสิ่งที่คุณไม่ทราบเกี่ยวกับชนิด ตามที่กล่าวไว้ข้างต้นauto&&จะให้ "การอ้างอิงสากล" ดังนั้นสิ่งอื่น ๆ ที่นอกเหนือจากนั้นจะทำให้คุณได้รับประเภทที่เฉพาะเจาะจงมากขึ้น ถ้าvสมมติว่าเป็น a vectorคุณสามารถทำได้decltype(v)::value_type&&ซึ่งเป็นสิ่งที่ฉันคิดว่าคุณต้องการโดยรับผลลัพธ์ของoperator*ประเภทตัววนซ้ำ คุณสามารถdecltype(begin(v))::value_type&&ตรวจสอบประเภทของตัววนซ้ำแทนคอนเทนเนอร์ได้เช่นกัน หากเรามีข้อมูลเชิงลึกเล็กน้อยเกี่ยวกับประเภทนี้เราอาจพิจารณาให้ชัดเจนขึ้นเล็กน้อยที่จะใช้auto&&...
cyberbisson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.