ฉันเพิ่งติดตามการสนทนา Reddit ซึ่งนำไปสู่การเปรียบเทียบที่ดีของstd::visit
การเพิ่มประสิทธิภาพในคอมไพเลอร์ ฉันสังเกตเห็นสิ่งต่อไปนี้: https://godbolt.org/z/D2Q5ED
ทั้ง GCC9 และ Clang9 (ฉันเดาว่าพวกเขาแบ่งปัน stdlib เดียวกัน) ไม่ได้สร้างรหัสสำหรับการตรวจสอบและการโยนข้อยกเว้นที่ไม่มีค่าเมื่อทุกประเภทเป็นไปตามเงื่อนไขบางประการ สิ่งนี้นำไปสู่วิธีที่ดีกว่า codegen ดังนั้นฉันยกปัญหากับ MSVC STL และถูกนำเสนอด้วยรหัสนี้:
template <class T>
struct valueless_hack {
struct tag {};
operator T() const { throw tag{}; }
};
template<class First, class... Rest>
void make_valueless(std::variant<First, Rest...>& v) {
try { v.emplace<0>(valueless_hack<First>()); }
catch(typename valueless_hack<First>::tag const&) {}
}
การอ้างสิทธิ์นี้ทำให้สิ่งต่าง ๆ ไร้ค่าและการอ่านเอกสารนั้นควร:
ก่อนอื่นให้ทำลายค่าที่มีอยู่ในปัจจุบัน (ถ้ามี) จากนั้นกำหนดค่าเริ่มต้นให้ตรงตามที่มีอยู่ราวกับว่าการสร้างค่าประเภท
T_I
ด้วยอาร์กิวเมนต์std::forward<Args>(args)....
หากมีการโยนข้อยกเว้น*this
อาจกลายเป็น
สิ่งที่ฉันไม่เข้าใจ: เหตุใดจึงระบุว่า "อาจ" การอยู่ในสภาพเดิมถูกต้องตามกฎหมายหรือไม่หากการดำเนินการทั้งหมดเกิดขึ้น? เพราะนี่คือสิ่งที่ GCC ทำ:
// For suitably-small, trivially copyable types we can create temporaries
// on the stack and then memcpy them into place.
template<typename _Tp>
struct _Never_valueless_alt
: __and_<bool_constant<sizeof(_Tp) <= 256>, is_trivially_copyable<_Tp>>
{ };
และในภายหลังมันก็มีเงื่อนไขดังนี้:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);
ดังนั้นโดยทั่วไปจะสร้างชั่วคราวและหากประสบความสำเร็จคัดลอก / ย้ายมันเข้าไปในสถานที่จริง
IMO นี่เป็นการละเมิด "ขั้นแรกทำลายค่าที่มีอยู่ในปัจจุบัน" ตามที่ระบุไว้โดย docu เมื่อฉันอ่านมาตรฐานหลังจากนั้นv.emplace(...)
ค่าปัจจุบันในชุดตัวเลือกจะถูกทำลายเสมอและประเภทใหม่เป็นประเภทชุดหรือไม่มีค่า
ฉันจะได้รับเงื่อนไขที่is_trivially_copyable
ไม่รวมทุกประเภทที่มีตัวทำลายที่สังเกตได้ ดังนั้นสิ่งนี้อาจเป็นได้ว่า: "ตัวแปร as-if ถูกกำหนดค่าเริ่มต้นใหม่ด้วยค่าเก่า" หรือดังนั้น แต่สถานะของตัวแปรนั้นเป็นผลที่สังเกตได้ ดังนั้นมาตรฐานจะอนุญาตอย่างแน่นอนซึ่งemplace
ไม่เปลี่ยนค่าปัจจุบันหรือไม่
แก้ไขตามคำพูดมาตรฐาน:
จากนั้นเริ่มต้นค่าที่มีอยู่เช่นถ้าตรงที่ไม่ใช่รายการเริ่มต้นค่าของประเภท TI
std::forward<Args>(args)...
กับข้อโต้แย้ง
ไม่T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
จริงๆนับเป็นการดำเนินงานที่ถูกต้องดังกล่าวข้างต้น? นี่คือสิ่งที่มีความหมายโดย "ราวกับว่า"?
might/may
ถ้อยคำเนื่องจากมาตรฐานไม่ได้ระบุว่าทางเลือกคืออะไร