std :: bit_cast พร้อม std :: array


14

ในการพูดคุยล่าสุดของเขา“ชนิดเล่นสำนวนในปัจจุบัน c ++”มู Doumler กล่าวว่าstd::bit_castไม่สามารถนำมาใช้เพื่อบิตโยนfloatลงไปunsigned char[4]เพราะอาร์เรย์แบบ C ไม่สามารถกลับมาจากฟังก์ชั่น เราควรใช้std::memcpyหรือรอจนกว่า C ++ 23 (หรือหลังจากนั้น) เมื่อสิ่งที่ชอบreinterpret_cast<unsigned char*>(&f)[i]จะกลายเป็นชัดเจน

ใน C ++ 20 เราสามารถใช้std::arrayกับstd::bit_cast,

float f = /* some value */;
auto bits = std::bit_cast<std::array<unsigned char, sizeof(float)>>(f);

แทนที่จะเป็นอาร์เรย์แบบ C เพื่อรับจำนวนไบต์float?

คำตอบ:


15

ใช่มันใช้งานได้กับคอมไพเลอร์รายใหญ่ทั้งหมดและเท่าที่ฉันสามารถบอกได้จากการดูมาตรฐานมันสามารถพกพาได้และรับประกันว่าจะทำงานได้

ก่อนอื่นstd::array<unsigned char, sizeof(float)>รับประกันได้ว่าเป็นผลรวม ( https://eel.is/c++draft/array#overview-2 ) จากสิ่งนี้ตามมาว่ามันมีsizeof(float)จำนวนcharภายในอย่างแน่นอน (โดยทั่วไปเป็น a char[], แม้ว่า afaics มาตรฐานจะไม่บังคับใช้การดำเนินการนี้โดยเฉพาะ - แต่มันบอกว่าองค์ประกอบจะต้องต่อเนื่องกัน) และไม่สามารถมีสมาชิกที่ไม่คงที่เพิ่มเติมได้

ดังนั้นจึงคัดลอกได้เล็กน้อยและขนาดของมันตรงกับของfloatเช่นกัน

คุณสมบัติทั้งสองนั้นอนุญาตให้คุณใช้bit_castระหว่างกันได้


3
โปรดทราบว่าstruct X { unsigned char elems[5]; };เป็นไปตามกฎที่คุณอ้างถึง สามารถเริ่มต้นรายการได้อย่างแน่นอนด้วยองค์ประกอบสูงสุด 4 รายการ นอกจากนี้ยังสามารถเริ่มต้นรายการได้ด้วย 5 องค์ประกอบ ฉันไม่คิดว่าผู้ใช้งานไลบรารี่มาตรฐานจะเกลียดผู้คนมากพอที่จะทำสิ่งนี้ได้จริง แต่ฉันคิดว่ามันสอดคล้องกับทางเทคนิค
Barry

ขอบคุณ! - Barry ฉันไม่คิดว่ามันค่อนข้างถูก มาตรฐานบอกว่า: "สามารถเริ่มต้นรายการได้ด้วยองค์ประกอบสูงสุด N" การตีความของฉันคือ "ถึง" หมายถึง "ไม่เกิน" elems[5]ซึ่งหมายความว่าคุณไม่สามารถทำ และ ณ จุดนั้นฉันไม่สามารถเห็นว่าคุณจะจบลงด้วยการรวมที่ไหนsizeof(array<char, sizeof(T)>) != sizeof(T)?
Timur Doumler

ฉันเชื่อว่าวัตถุประสงค์ของกฎ ("การรวมที่สามารถเริ่มต้นรายการได้ ... ") คือการอนุญาตอย่างใดอย่างหนึ่งstruct X { unsigned char c1, c2, c3, c4; };หรือstruct X { unsigned char elems[4]; };- ดังนั้นในขณะที่ chars จำเป็นต้องเป็นองค์ประกอบของการรวมนั้นสิ่งนี้ช่วยให้พวกเขาเป็นองค์ประกอบรวมโดยตรง หรือองค์ประกอบของการรวมย่อยเดี่ยว
Timur Doumler

2
@ Timur "ถึง" ไม่ได้แปลว่า "ไม่เกิน" ในทำนองเดียวกันความหมายP -> Qไม่ได้บ่งบอกอะไรเกี่ยวกับกรณีที่!P
Barry

1
แม้ว่าการรวมจะไม่มีอะไรเลยนอกจากอาเรย์ขององค์ประกอบ 4 ประการแน่ชัดก็ไม่อาจรับประกันได้ว่าarrayตัวเองจะไม่มีช่องว่างภายใน การใช้งานของมันอาจไม่มีการขยาย (และการใช้งานใด ๆ ที่ควรพิจารณาว่าผิดปกติ) แต่ไม่มีการรับประกันว่าarrayตัวเองจะไม่
Nicol Bolas

6

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

ต่อ[อาร์เรย์] / 1-3 :

ส่วนหัว<array>กำหนดแม่แบบคลาสสำหรับการจัดเก็บลำดับขนาดคงที่ของวัตถุ อาร์เรย์เป็นคอนเทนเนอร์ที่อยู่ติดกัน อินสแตนซ์ขององค์ประกอบของarray<T, N>ร้านค้าNประเภทTดังนั้นจึงsize() == Nเป็นค่าคงที่

อาเรย์คือการรวมที่สามารถถูกลิสต์เริ่มต้นได้จนถึงN องค์ประกอบที่มีชนิดที่สามารถแปลงTได้

อาร์เรย์ตรงตามข้อกำหนดทั้งหมดของคอนเทนเนอร์และคอนเทนเนอร์ที่ย้อนกลับได้ ( [container.requirements]) ยกเว้นว่าวัตถุอาร์เรย์ที่สร้างเป็นค่าเริ่มต้นจะไม่ว่างเปล่าและการสลับนั้นไม่มีความซับซ้อนคงที่ อาร์เรย์ตรงตามข้อกำหนดบางประการของคอนเทนเนอร์ลำดับ คำอธิบายมีไว้ที่นี่สำหรับการดำเนินการกับอาร์เรย์ที่ไม่ได้อธิบายไว้ในตารางใดตารางหนึ่งเหล่านี้และสำหรับการดำเนินการที่มีข้อมูลความหมายเพิ่มเติม

มาตรฐานไม่จริงต้องstd::arrayที่จะมีอีกหนึ่งสมาชิกข้อมูลสาธารณะประเภทT[N]ดังนั้นในทางทฤษฎีมันเป็นไปได้ว่าหรือsizeof(To) != sizeof(From)is_­trivially_­copyable_­v<To>

ฉันจะแปลกใจถ้ามันใช้งานไม่ได้จริง ๆ


2

ใช่.

จากบทความที่อธิบายพฤติกรรมของstd::bit_castและการนำเสนอไปใช้เท่าที่ทั้งสองประเภทมีขนาดเท่ากันและคัดลอกได้เล็กน้อยนักแสดงควรจะประสบความสำเร็จ

การปรับใช้ที่ง่ายขึ้นของstd::bit_castควรเป็นดังนี้:

template <class Dest, class Source>
inline Dest bit_cast(Source const &source) {
    static_assert(sizeof(Dest) == sizeof(Source));
    static_assert(std::is_trivially_copyable<Dest>::value);
    static_assert(std::is_trivially_copyable<Source>::value);

    Dest dest;
    std::memcpy(&dest, &source, sizeof(dest));
    return dest;
}

ตั้งแต่ float (4 bytes) และ array ของunsigned charด้วยsize_of(float)ความเคารพ asserts เหล่านั้นทั้งหมดพื้นฐานstd::memcpyจะถูกดำเนินการ ดังนั้นแต่ละองค์ประกอบในอาร์เรย์ผลลัพธ์จะเป็นหนึ่งไบต์ต่อเนื่องของการลอย

เพื่อพิสูจน์พฤติกรรมนี้ผมเขียนเป็นตัวอย่างเล็ก ๆ ในคอมไพเลอร์ Explorer ที่คุณสามารถลองที่นี่: https://godbolt.org/z/4G21zS ลอย 5.0 จะถูกเก็บไว้อย่างถูกต้องเป็นอาร์เรย์ของไบต์ ( Ox40a00000) สอดคล้องกับที่แทนเลขฐานสิบหกจำนวนลอยว่าในบิ๊ก Endian


คุณแน่ใจหรือไม่ว่าstd::arrayได้รับการรับประกันว่าจะไม่มีช่องว่างภายใน ฯลฯ
LF

1
น่าเสียดายที่ความจริงที่ว่าบางรหัสทำงานไม่ได้แปลว่าไม่มี UB อยู่ในนั้น ตัวอย่างเช่นเราสามารถเขียนauto bits = reinterpret_cast<std::array<unsigned char, sizeof(float)>&>(f)และรับผลลัพธ์เดียวกัน มันพิสูจน์อะไรไหม?
Evg

@LF ตามข้อกำหนด: std::arrayตอบสนองความต้องการของContiguiosContainer (ตั้งแต่ C ++ 17)
มานูเอลกิล

1
@ManuelGil: std::vectorเป็นไปตามเกณฑ์เดียวกันและเห็นได้ชัดว่าไม่สามารถใช้ที่นี่ มีบางสิ่งที่จำเป็นต้องใช้ที่std::arrayเก็บองค์ประกอบภายในชั้นเรียน (ในเขต) ป้องกันไม่ให้มันเป็นตัวชี้ที่เรียบง่ายเพื่ออาร์เรย์ภายใน? (เช่นเดียวกับในเวกเตอร์ซึ่งมีขนาดซึ่งไม่จำเป็นต้องมีอาร์เรย์ในฟิลด์)
firda

@firda ความต้องการโดยรวมของการstd::arrayใช้งานอย่างมีประสิทธิภาพนั้นจำเป็นต้องใช้เพื่อจัดเก็บองค์ประกอบภายใน แต่ฉันกังวลเกี่ยวกับปัญหาการจัดวาง
LF
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.