std :: tie ทำงานอย่างไร?


120

ฉันใช้std::tieโดยไม่ต้องคิดมากกับมัน มันใช้งานได้ดังนั้นฉันจึงยอมรับว่า:

auto test()
{
   int a, b;
   std::tie(a, b) = std::make_tuple(2, 3);
   // a is now 2, b is now 3
   return a + b; // 5
}

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

คำตอบ:


152

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

int a;
std::tie(a) = std::make_tuple(24);
return a; // 24

สิ่งที่เราต้องรู้เพื่อก้าวไปข้างหน้า:

  • std::tie สร้างและส่งคืนการอ้างอิงจำนวนมาก
  • std::tuple<int>และstd::tuple<int&>2 เรียนแตกต่างกันอย่างสิ้นเชิงกับการเชื่อมต่อระหว่างพวกเขาไม่มีอื่น ๆ std::tupleที่พวกเขาถูกสร้างขึ้นจากแม่แบบเดียวกัน
  • ทูเปิลมีการoperator=ยอมรับทูเปิลประเภทต่างๆ (แต่เป็นหมายเลขเดียวกัน) โดยที่สมาชิกแต่ละคนจะได้รับมอบหมายแยกกันจากcppreference :

    template< class... UTypes >
    tuple& operator=( const tuple<UTypes...>& other );
    

    (3) สำหรับผมทั้งหมดได้รับมอบหมายไปstd::get<i>(other)std::get<i>(*this)

ขั้นตอนต่อไปคือการกำจัดฟังก์ชันเหล่านั้นที่ขวางทางคุณเท่านั้นดังนั้นเราจึงสามารถแปลงรหัสของเราให้เป็นดังนี้

int a;
std::tuple<int&>{a} = std::tuple<int>{24};
return a; // 24

ขั้นตอนต่อไปคือการดูว่าเกิดอะไรขึ้นภายในโครงสร้างเหล่านั้น สำหรับสิ่งนี้ฉันสร้างTสารทดแทน2 ประเภทสำหรับstd::tuple<int>และTrสารทดแทนstd::tuple<int&>ซึ่งลดลงเหลือน้อยที่สุดสำหรับการดำเนินการของเรา:

struct T { // substituent for std::tuple<int>
    int x;
};

struct Tr { // substituent for std::tuple<int&>
    int& xr;

    auto operator=(const T& other)
    {
       // std::get<I>(*this) = std::get<I>(other);
       xr = other.x;
    }
};

auto foo()
{
    int a;
    Tr{a} = T{24};

    return a; // 24
}

และในที่สุดฉันก็ชอบที่จะกำจัดโครงสร้างทั้งหมดด้วยกัน (มันไม่เทียบเท่า 100% แต่มันใกล้พอสำหรับเราและชัดเจนพอที่จะอนุญาต):

auto foo()
{
    int a;

    { // block substituent for temporary variables

    // Tr{a}
    int& tr_xr = a;

    // T{24}
    int t_x = 24;

    // = (asignement)
    tr_xr = t_x;
    }

    return a; // 24
}

ดังนั้นโดยทั่วไปเริ่มต้นการอ้างอิงข้อมูลสมาชิกไปstd::tie(a) สร้างสมาชิกข้อมูลที่มีค่าและการกำหนดจะกำหนด 24 ให้กับการอ้างอิงสมาชิกข้อมูลในโครงสร้างแรก แต่เนื่องจากว่าข้อมูลสมาชิกคือการอ้างอิงผูกไว้กับที่โดยทั่วไปได้รับมอบหมายไปastd::tuple<int>(24)24a24a


1
สิ่งที่ทำให้ฉันมีข้อบกพร่องคือเรากำลังเรียกตัวดำเนินการกำหนดค่า rvalue
อดัมซาห์ราน

ในคำตอบนี้ระบุว่าคอนเทนเนอร์ไม่สามารถอ้างอิงได้ เหตุใดจึงtupleสามารถอ้างอิงได้
nn0p

6
@ nn0p std::tupleไม่ใช่คอนเทนเนอร์อย่างน้อยก็ไม่ได้อยู่ในคำศัพท์ C ++ ไม่ใช่เช่นเดียวกับstd::vectorและไลค์ ตัวอย่างเช่นคุณไม่สามารถทำซ้ำด้วยวิธีปกติบนทูเปิลได้เนื่องจากมีวัตถุประเภทต่างๆ
bolov

@ อดัมผูก (x, y) = make_pair (1,2); จริงกลายเป็น std :: tie (x, y) .operator = (std :: make_pair (1, 2)) จึงเป็นเหตุผลว่าทำไม "การกำหนดค่า rvalue" จึงทำงาน XD
Ju Piece

30

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

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

(โปรดสังเกตว่าในตัวสร้าง C ++ 17 ทำการหักอาร์กิวเมนต์ดังนั้น make_tupleค่อนข้างไม่จำเป็นเช่นกัน)

int a, b;
std::tie(a, b) = std::make_tuple(2, 3);

// C++17
auto  [c, d] = std::make_tuple(4, 5);
auto  [e, f] = std::tuple(6, 7);
std::tuple t(8,9); auto& [g, h] = t; // not possible with std::tie

2
หากบรรทัดสุดท้ายรวบรวมฉันก็กังวลเล็กน้อย ดูเหมือนการผูกการอ้างอิงกับชั่วคราวซึ่งผิดกฎหมาย
Nir Friedman

3
@Neil จะต้องเป็นการอ้างอิง rvalue หรือการอ้างอิง const lvalue คุณไม่สามารถผูกการอ้างอิง lvalue กับ prvalue (ชั่วคราว) แม้ว่านี่จะเป็น "ส่วนขยาย" ใน MSVC มานานแล้ว
Nir Friedman

1
อาจเป็นเรื่องที่ควรค่าแก่การกล่าวถึงว่าtieการผูกแบบมีโครงสร้างสามารถใช้วิธีนี้กับประเภทที่ไม่สามารถสร้างได้ตามค่าเริ่มต้น
แดน

5
ใช่std::tie()มีประโยชน์น้อยกว่ามากตั้งแต่ C ++ 17 ซึ่งการเชื่อมโยงแบบมีโครงสร้างมักจะเหนือกว่า แต่ก็ยังใช้งานได้รวมถึงการกำหนดให้กับตัวแปรที่มีอยู่ (ไม่ได้ประกาศใหม่พร้อมกัน) และทำสิ่งอื่น ๆ อย่างรัดกุมเช่นการสลับหลายตัวแปรหรือสิ่งอื่น ๆ ที่ ต้องกำหนดให้กับการอ้างอิง
underscore_d
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.