ฉันมีเสื้อคลุมสำหรับรหัสดั้งเดิมบางส่วน
class A{
L* impl_; // the legacy object has to be in the heap, could be also unique_ptr
A(A const&) = delete;
L* duplicate(){L* ret; legacy_duplicate(impl_, &L); return ret;}
... // proper resource management here
};
ในรหัสดั้งเดิมนี้ฟังก์ชั่นที่ "ทำซ้ำ" วัตถุไม่ปลอดภัยเธรด (เมื่อเรียกอาร์กิวเมนต์แรกเดียวกัน) ดังนั้นมันจึงไม่ถูกทำเครื่องหมายconstใน wrapper ฉันเดากฎต่อไปนี้ที่ทันสมัย: https://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/
นี้ดูเหมือนจะเป็นวิธีที่ดีที่จะใช้ตัวสร้างสำเนายกเว้นสำหรับรายละเอียดมันที่ไม่ได้เป็นduplicate constดังนั้นฉันไม่สามารถทำสิ่งนี้โดยตรง:
class A{
L* impl_; // the legacy object has to be in the heap
A(A const& other) : L{other.duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
ดังนั้นวิธีการออกสถานการณ์ขัดแย้งนี้คืออะไร?
(สมมุติว่ายังlegacy_duplicateไม่ปลอดภัยเธรด แต่ฉันรู้ว่าปล่อยวัตถุในสถานะเดิมเมื่อมันออกจากการเป็น C-function พฤติกรรมจะได้รับการบันทึกไว้เท่านั้น แต่ไม่มีแนวคิดเรื่องความมั่นคง)
ฉันสามารถคิดถึงสถานการณ์ที่เป็นไปได้มากมาย:
(1)ความเป็นไปได้อย่างหนึ่งคือไม่มีวิธีใช้ตัวสร้างสำเนาที่มีความหมายตามปกติเลย (ใช่ฉันสามารถย้ายวัตถุและนั่นไม่ใช่สิ่งที่ฉันต้องการ)
(2)ในทางกลับกันการคัดลอกวัตถุนั้นไม่ปลอดภัยโดยเธรดในแง่ที่ว่าการทำสำเนาแบบง่าย ๆ สามารถค้นหาแหล่งที่มาในสภาพที่ถูกดัดแปลงครึ่งดังนั้นฉันสามารถไปข้างหน้าและทำสิ่งนี้ได้
class A{
L* impl_;
A(A const& other) : L{const_cast<A&>(other).duplicate()}{} // error calling a non-const function
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
(3)หรือเพียงแค่ประกาศค่าคงที่duplicateและความปลอดภัยของเธรดในทุกบริบท (หลังจากฟังก์ชั่นดั้งเดิมทั้งหมดไม่สนใจconstดังนั้นคอมไพเลอร์จะไม่บ่น)
class A{
L* impl_;
A(A const& other) : L{other.duplicate()}{}
L* duplicate() const{L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
(4)ในที่สุดฉันสามารถทำตามตรรกะและสร้างตัวคัดลอกที่ใช้อาร์กิวเมนต์ที่ไม่ใช่ const
class A{
L* impl_;
A(A const&) = delete;
A(A& other) : L{other.duplicate()}{}
L* duplicate(){L* ret; legacy_duplicate(impl_, &ret); return ret;}
};
แต่กลับกลายเป็นว่างานนี้หลาย ๆ constบริบทเพราะวัตถุเหล่านี้มักจะไม่
คำถามคือมันเป็นเส้นทางที่ถูกต้องหรือทั่วไป?
ฉันไม่สามารถตั้งชื่อพวกเขาได้ แต่ฉันคาดหวังว่าจะมีปัญหามากมายตามทางที่จะมีคอนสตรัคเตอร์ที่ไม่ใช่แบบคอน อาจเป็นไปได้ว่ามันจะไม่ถือว่าเป็นประเภทค่าเนื่องจากความละเอียดนี้
(5)ในที่สุดแม้ว่าสิ่งนี้จะเกินความจริงและอาจมีค่าใช้จ่ายสูงชัน แต่ฉันสามารถเพิ่ม mutex ได้:
class A{
L* impl_;
A(A const& other) : L{other.duplicate_locked()}{}
L* duplicate(){
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
L* duplicate_locked() const{
std::lock_guard<std::mutex> lk(mut);
L* ret; legacy_duplicate(impl_, &ret); return ret;
}
mutable std::mutex mut;
};
แต่การถูกบังคับให้ทำสิ่งนี้ดูเหมือนว่าจะทำให้เป็นโมฆะและทำให้ชั้นเรียนใหญ่ขึ้น ฉันไม่แน่ใจ. ฉันกำลังโน้มตัวไปยัง(4)หรือ(5)หรือทั้งสองอย่างรวมกัน
แก้ไข 1:
ตัวเลือกอื่น:
(6)ลืมความรู้สึกที่ไม่ซ้ำกันของฟังก์ชันสมาชิกที่ซ้ำกันทั้งหมดและเพียงแค่เรียกlegacy_duplicateจากตัวสร้างและประกาศว่าตัวสร้างการคัดลอกไม่ปลอดภัยเธรด (และถ้าจำเป็นให้เพิ่มเธรดที่ปลอดภัยต่อเธรดอีกประเภทA_mt)
class A{
L* impl_;
A(A const& other){legacy_duplicate(other.impl_, &impl_);}
};
แก้ไข 2:
นี่อาจเป็นแบบอย่างที่ดีสำหรับสิ่งที่ฟังก์ชั่นดั้งเดิมทำ โปรดทราบว่าด้วยการแตะอินพุตการโทรจะไม่ปลอดภัยสำหรับเธรดตามค่าที่แสดงโดยอาร์กิวเมนต์แรก
void legacy_duplicate(L* in, L** out){
*out = new L{};
char tmp = in[0];
in[0] = tmp;
std::memcpy(*out, in, sizeof *in); return;
}
แก้ไข 3:
ฉันได้เรียนรู้เมื่อเร็ว ๆ นี้ว่าstd::auto_ptrมีปัญหาคล้ายกันในการมีตัวสร้าง "คัดลอก" ที่ไม่ใช่ const ผลคือauto_ptrไม่สามารถใช้ภายในคอนเทนเนอร์ได้ https://www.quantstart.com/articles/STL-Containers-and-Auto_ptrs-Why-They-Dont-Mix/
legacy_duplicateไม่สามารถเรียกด้วยอาร์กิวเมนต์แรกเดียวกันจากสองเธรดที่แตกต่างกัน
constจริง ๆ แล้วหมายถึงอะไร :-) ฉันไม่อยากจะคิดว่าสองครั้งเกี่ยวกับการconst&ใน ctor otherสำเนาของฉันตราบเท่าที่ฉันไม่แก้ไข ฉันมักจะนึกถึงความปลอดภัยของเธรดเป็นสิ่งที่เราจะต้องเข้าถึงได้จากหลาย ๆ หัวข้อผ่านการห่อหุ้มและฉันหวังว่าจะได้คำตอบจริงๆ
Lซึ่งถูกแก้ไขโดยการสร้างLอินสแตนซ์ใหม่หรือไม่? หากไม่เป็นเช่นนั้นคุณเชื่อว่าการดำเนินการนี้ไม่ปลอดภัยสำหรับเธรดหรือไม่