มีความแตกต่างอะไรระหว่างการใช้ struct และ std :: pair


26

ฉันเป็นโปรแกรมเมอร์ C ++ ที่มีประสบการณ์ จำกัด

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

Choice 1:
    map<int, pair<string, bool> >

Choice 2:
    struct Ente {
        string name;
        bool flag;
    }
    map<int, Ente>

โดยเฉพาะมีค่าใช้จ่ายใด ๆ ที่ใช้structแทนง่ายpairหรือไม่


18
A std::pair คือโครงสร้าง
Caleth

3
@gnat: คำถามทั่วไปเช่นนั้นไม่ค่อยเหมาะสำหรับเป้าหมายเฉพาะสำหรับคำถามเฉพาะเช่นคำถามนี้โดยเฉพาะอย่างยิ่งหากไม่มีคำตอบเฉพาะในเป้าหมายของ dupe (ซึ่งไม่น่าจะเกิดในกรณีนี้)
Robert Harvey

18
@Caleth - std::pairเป็นแม่แบบ std::pair<string, bool>เป็น struct
Pete Becker

4
pairไม่มีความหมายทั้งหมด ไม่มีใครอ่านรหัสของคุณ (รวมถึงคุณในอนาคต) จะรู้ว่าe.firstเป็นชื่อของบางสิ่งบางอย่างเว้นแต่คุณจะชี้ให้เห็นอย่างชัดเจน ฉันเป็นคนที่เชื่อมั่นในสิ่งที่pairน่าสงสารและขี้เกียจนอกจากนี้stdและเมื่อมันคิดว่าไม่มีใครคิดว่า "แต่บางวันทุกคนจะใช้มันสำหรับทุกสิ่งที่เป็นสองสิ่งและไม่มีใครรู้ว่ารหัสของใครหมายถึงอะไร "
Jason C

2
@Snowman โอ้แน่นอน ถึงกระนั้นมันก็เลวร้ายเกินไปเช่นตัวmapวนซ้ำไม่มีข้อยกเว้นที่ถูกต้อง ( "ครั้งแรก" = ที่สำคัญและ "สอง" value = ... จริงๆstdเหรอ?)
เจสันซี

คำตอบ:


33

ตัวเลือกที่ 1 ก็โอเคสำหรับสิ่งเล็ก ๆ "ใช้เพียงครั้งเดียว" โดยพื้นฐานแล้วstd::pairยังคงเป็นโครงสร้าง ตามที่ระบุไว้โดยตัวเลือกความคิดเห็นนี้ 1 จะนำไปสู่รหัสที่น่าเกลียดจริงๆที่บางแห่งลงไปในช่องกระต่ายthing.second->first.second->secondและไม่มีใครอยากถอดรหัสมัน

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


15

ประสิทธิภาพ :

มันขึ้นอยู่กับ.

ในกรณีเฉพาะของคุณจะไม่มีความแตกต่างของประสิทธิภาพเนื่องจากทั้งสองจะถูกวางในหน่วยความจำในทำนองเดียวกัน

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

struct Empty {};
struct Thing { std::string name; Empty e; };

int main() {
    std::cout << sizeof(std::string) << "\n";
    std::cout << sizeof(std::tuple<std::string, Empty>) << "\n";
    std::cout << sizeof(std::pair<std::string, Empty>) << "\n";
    std::cout << sizeof(Thing) << "\n";
}

พิมพ์: 32, 32, 40, 40 ideone

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


การอ่าน :

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

ฉันหมายความว่าmap[k].firstไม่ใช่เรื่องเลวร้ายในขณะที่get<0>(map[k])แทบจะไม่สามารถเข้าใจได้ ความคมชัดmap[k].nameที่แสดงถึงสิ่งที่เรากำลังอ่านในทันที

ทุกอย่างมีความสำคัญมากขึ้นเมื่อประเภทสามารถแปลงให้เป็นประเภทอื่นได้เนื่องจากการสลับเปลี่ยนเป็นประเภทที่ไม่ได้ตั้งใจกลายเป็นปัญหาที่แท้จริง

คุณอาจต้องการอ่านเกี่ยวกับ Structural vs Nominal Typing Enteเป็นประเภทเฉพาะที่สามารถดำเนินการได้โดยสิ่งที่คาดหวังEnteอะไรก็ตามที่สามารถทำงานได้std::pair<std::string, bool>สามารถทำงานกับพวกเขาได้ ... แม้ว่าจะมีstd::stringหรือboolไม่มีสิ่งที่พวกเขาคาดหวังเพราะstd::pairไม่มีความหมายที่เกี่ยวข้องกับมัน


การบำรุงรักษา :

ในแง่ของการบำรุงรักษาpairที่เลวร้ายที่สุดคือ คุณไม่สามารถเพิ่มฟิลด์

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

structเป็นผู้ชนะที่ชัดเจน คุณสามารถเพิ่มฟิลด์ได้ทุกที่ที่ต้องการ


สรุปแล้ว:

  • pair เป็นสิ่งที่เลวร้ายที่สุดของทั้งสองโลก
  • tuple อาจมีขอบเล็กน้อยในกรณีที่เฉพาะเจาะจงมาก (ประเภทที่ว่างเปล่า)
  • structใช้

หมายเหตุ: หากคุณใช้ getters แล้วคุณสามารถใช้ฐานที่ว่างเปล่าหลอกตัวเองโดยไม่ต้องลูกค้าต้องรู้เกี่ยวกับมันเช่นเดียวกับในstruct Thing: Empty { std::string name; }; ซึ่งเป็นเหตุผลว่าทำไมEncapsulationจึงเป็นหัวข้อถัดไปที่คุณควรคำนึงถึง


3
คุณไม่สามารถใช้ EBO เป็นคู่หากคุณทำตามมาตรฐาน องค์ประกอบของคู่จะถูกเก็บไว้ในสมาชิก firstและsecondไม่มีที่สำหรับการเพิ่มประสิทธิภาพฐานว่างเปล่าที่จะเตะเข้ามา
Revolver_Ocelot

2
@Revolver_Ocelot: ดีคุณไม่สามารถเขียน C ++ pairที่จะใช้ EBO ได้ แต่คอมไพเลอร์สามารถจัดหาบิวด์อินได้ เนื่องจากสิ่งเหล่านั้นควรจะเป็นสมาชิกอย่างไรก็ตามอาจสังเกตได้ (เช่นการตรวจสอบที่อยู่เป็นต้น) ซึ่งในกรณีนี้จะไม่สอดคล้อง
Matthieu M.

1
เพิ่ม C ++ 20 [[no_unique_address]], ซึ่งเทียบเท่ากับ EBO สำหรับสมาชิก
underscore_d

3

Pair จะส่องแสงมากที่สุดเมื่อใช้เป็นชนิดส่งคืนของฟังก์ชันพร้อมกับการกำหนดแบบ destructured โดยใช้ std :: tie และการโยงแบบโครงสร้างของ C ++ 17 ใช้ std :: tie:

struct Ente {/*...*/};
std::map<int, Ente> map;
auto inserted_position = map.end();
auto was_inserted = false;
std::tie(inserted_position, was_inserted) = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

ใช้การโยงโครงสร้างของ C ++ 17:

struct Ente {/*...*/};
std::map<int, Ente> map;
auto [inserted_position, was_inserted] = map.emplace(1, Ente{});
if (!was_inserted) {
    //handle insertion error
}

ตัวอย่างที่ไม่ดีของการใช้ std :: pair (หรือ tuple) จะเป็นดังนี้:

using player_data = std::tuple<std::string, uint64_t, double>;
player_data player{};
/* ... */
auto health = std::get<2>(player);
/* ... */

เพราะมันจะไม่ชัดเจนเมื่อโทรมาตรฐาน :: รับ <2> (player_data) สิ่งที่จะถูกเก็บไว้ที่ดัชนีตำแหน่ง 2. จำไว้ว่าการอ่านและทำให้มันเป็นที่ชัดเจนสำหรับผู้อ่านสิ่งที่รหัสจะทำคือสิ่งที่สำคัญ พิจารณาว่านี่สามารถอ่านได้มากขึ้น:

struct player_data
{
    std::string name;
    uint64_t player_id;
    double current_health;
};
player_data player{};
/* ... */
auto health = player.current_health;
/* ... */

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

ส่วนที่เกี่ยวข้องของหลักเกณฑ์หลัก:

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