polymorphic_allocator: ฉันควรใช้เมื่อใดและทำไม


122

นี่เป็นวิธีการในcppreference , นี่คือร่างการทำงาน

ฉันต้องยอมรับว่าฉันไม่เข้าใจว่าจุดประสงค์ที่แท้จริงคืออะไรpolymorphic_allocatorและเมื่อไหร่ / ทำไม / ฉันควรใช้มันอย่างไร
ตัวอย่างเช่นpmr::vectorมีลายเซ็นดังต่อไปนี้:

namespace pmr {
    template <class T>
    using vector = std::vector<T, polymorphic_allocator<T>>;
}

อะไรpolymorphic_allocatorข้อเสนอ? std::pmr::vectorข้อเสนออะไรเช่นกันในเรื่องของสมัยเก่าstd::vector? ฉันจะทำอะไรได้บ้างที่ฉันไม่สามารถทำได้จนถึงตอนนี้?
วัตถุประสงค์ที่แท้จริงของผู้จัดสรรนั้นคืออะไรและเมื่อใดที่ฉันควรใช้จริง?


1
พวกเขาพยายามเอาชนะปัญหาบางอย่าง allocator<T>โดยเนื้อแท้ ดังนั้นคุณจะเห็นคุณค่าหากคุณใช้ตัวจัดสรรบ่อยๆ
edmz

2
ที่เกี่ยวข้องกระดาษ
edmz

คำตอบ:


103

ใบเสนอราคาจาก cppreference:

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

ปัญหาเกี่ยวกับตัวจัดสรร "ปกติ" คือพวกเขาเปลี่ยนประเภทของคอนเทนเนอร์ หากคุณต้องการvectorด้วยตัวจัดสรรเฉพาะคุณสามารถใช้Allocatorพารามิเตอร์เทมเพลต:

auto my_vector = std::vector<int,my_allocator>();

ปัญหาตอนนี้คือเวกเตอร์นี้ไม่ใช่ประเภทเดียวกับเวกเตอร์ที่มีตัวจัดสรรที่แตกต่างกัน คุณไม่สามารถส่งต่อไปยังฟังก์ชันที่ต้องใช้เวกเตอร์ตัวจัดสรรเริ่มต้นหรือกำหนดเวกเตอร์สองตัวที่มีประเภทตัวจัดสรรที่แตกต่างกันให้กับตัวแปร / ตัวชี้เดียวกันเช่น:

auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error

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

การปรับแต่งพฤติกรรมผู้จัดสรรทำได้โดยให้ผู้จัดสรร a std::memory_resource *:

// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);

// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);

auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
      // my_vector and my_other_vector have same type

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

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

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

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


2
ดังนั้นรูปแบบไบนารีสำหรับstd::pmr::คลาสมีแนวโน้มที่จะแตกต่างกันมากหรือไม่?
Euri Pinhollow

12
@EuriPinhollow คุณไม่สามารถอยู่reinterpret_castระหว่าง a std::vector<X>และstd::pmr::vector<X>ถ้านั่นคือสิ่งที่คุณถาม
davmac

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

1
@ Yakk-AdamNevraumont "ซึ่งเป็นstd::pmr::ภาชนะที่ยังไม่สามารถเข้ากันได้กับเทียบเท่าstd::ภาชนะที่ใช้ตัวจัดสรรเริ่มต้น" ไม่มีการกำหนดตัวดำเนินการกำหนดจากที่หนึ่งไปยังอีก หากมีข้อสงสัยให้ลองใช้: godbolt.org/z/Q5BKev (โค้ดไม่ตรงตามข้างบนเนื่องจาก gcc / clang มีคลาสการจัดสรรโพลีมอร์ฟิกในเนมสเปซ "ทดลอง")
davmac

1
@davmac อ่าไม่มีคอนtemplate<class OtherA, std::enable_if< A can be constructed from OtherA > vector( vector<T, OtherA>&& )สตรัคเตอร์ ฉันไม่แน่ใจและไม่รู้ว่าจะหาคอมไพเลอร์ที่มี pmr ตาม TS ได้ที่ไหน
Yakk - Adam Nevraumont

33

polymorphic_allocatorคือตัวจัดสรรแบบกำหนดเองตามstd::functionที่เรียกใช้ฟังก์ชันโดยตรง

มันช่วยให้คุณใช้ตัวจัดสรรกับคอนเทนเนอร์ของคุณได้โดยไม่ต้องตัดสินใจว่าจะประกาศอันไหน polymorphic_allocatorดังนั้นหากคุณมีสถานการณ์ที่มากกว่าหนึ่งจัดสรรจะมีความเหมาะสมที่คุณสามารถใช้

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

ก่อนอื่นคุณต้องมีรหัสที่ต้องการตัวจัดสรรจากนั้นคุณต้องต้องการที่จะสามารถสลับได้ว่าจะใช้อันไหนก่อนที่จะพิจารณาเวกเตอร์ pmr


7

คืนหนึ่งของ allocators polymorphic คือว่าเป็นเพียงแค่เสมอpolymorphic_allocator<T>::pointer T*นั่นหมายความว่าคุณไม่สามารถใช้กับพอยน์เตอร์แฟนซีได้ หากคุณต้องการทำบางสิ่งบางอย่างเช่นองค์ประกอบสถานที่ของvectorหน่วยความจำที่ใช้ร่วมกันและเข้าถึงผ่านboost::interprocess::offset_ptrsคุณต้องใช้ตัวจัดสรรที่ไม่ใช่โพลีมอร์ฟิกแบบเก่าสำหรับสิ่งนั้น

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


2
นี่คือประเด็นสำคัญและเป็นคนเกียจคร้านมาก กระดาษพอยน์เตอร์แฟนซีที่มีความหมายของ Arthur O'Dwyer สำรวจดินแดนเช่นเดียวกับหนังสือ "Mastering the c ++ 17 STL" ของเขา
เมื่อ

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