std :: std :: dizi ile bit_cast


14

Yaptığı son konuşma ise “Modern C Tipi cinaslı ++” Timur Doumler söyledi o std::bit_castbit atmak kullanılamaz floatbir içine unsigned char[4]C tarzı diziler bir işlev döndürülen edilemez çünkü. std::memcpyC ++ 23 (veya üstü) gibi bir şey reinterpret_cast<unsigned char*>(&f)[i]iyi tanımlanıncaya kadar kullanmalı veya beklemeliyiz .

C ++ 20'de bir std::arrayile kullanabilir miyiz std::bit_cast,

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

yerine C-tarzı bir dizi bayt almak için float?

Yanıtlar:


15

Evet, bu tüm büyük derleyiciler üzerinde çalışıyor ve standarda bakarak söyleyebildiğim kadarıyla taşınabilir ve çalışması garanti.

Her şeyden önce, std::array<unsigned char, sizeof(float)>bir agrega olması garanti edilir ( https://eel.is/c++draft/array#overview-2 ). Bundan, tam olarak bir sizeof(float)dizi chars char[]içerdiği görülür (tipik olarak afaics, standart bu özel uygulamayı zorunlu kılmaz - ancak elementlerin bitişik olması gerektiğini söyler) ve statik olmayan ek elemanlara sahip olamaz.

Bu nedenle önemsiz bir şekilde kopyalanabilir ve boyutu da ile eşleşir float.

Bu iki özellik, bit_castaralarında geçiş yapmanızı sağlar .


3
Belirttiğiniz struct X { unsigned char elems[5]; };kuralı karşıladığınızı unutmayın . Kesinlikle 4 adede kadar elemanla listeye başlatılabilir. Bu olabilir Ayrıca liste başlatıldı 5 elemanları ile olmak. Herhangi bir standart kütüphane uygulayıcının bunu yapmak için insanlardan yeterince nefret ettiğini düşünmüyorum, ancak teknik olarak uyumlu olduğunu düşünüyorum.
Barry

Teşekkürler! - Barry, bunun dođru olduđunu sanmýyorum. Standart şöyle diyor: "N öğeye kadar listeyle başlatılabilir". Benim yorumum "en fazla" ima etmek "en fazla ima ediyor. Bu, yapamayacağınız anlamına gelir elems[5]. Ve bu noktada, nerede bir agrega oluşturabileceğinizi göremiyorum sizeof(array<char, sizeof(T)>) != sizeof(T)?
Timur Doumler

Kuralın amacının ("liste olarak başlatılabilen bir toplam ...") ya struct X { unsigned char c1, c2, c3, c4; };da izin vermesi olduğuna inanıyorum struct X { unsigned char elems[4]; };- yani karakterlerin bu toplamın unsurları olması gerekirken, bu ya doğrudan toplam unsurlar olmalarına izin veriyor veya tek bir alt agrega elemanlarını içerebilir.
Timur Doumler

2
@ Timur "en fazla" anlamına gelmez "en fazla" anlamına gelmez. Aynı şekilde P -> Qima da dava hakkında hiçbir şey ima etmez!P
Barry

1
Toplama, tam olarak 4 öğeden oluşan bir diziden başka bir şey içermese bile, arraykendisinin dolguya sahip olmayacağının garantisi yoktur . Uygulamalarında dolgu bulunmayabilir (ve herhangi bir uygulama işlevsiz olarak kabul edilmelidir), ancak arraykendisinin olmayacağının garantisi yoktur .
Nicol Bolas

6

Hizalama ve doldurma sorunlarını göz önünde bulundurmadığı için kabul edilen cevap yanlış.

Başına [dizi] / 1-3 :

Üstbilgi <array>, sabit boyutlu nesne dizilerini saklamak için bir sınıf şablonu tanımlar. Dizi bitişik bir kaptır. Tür öğelerini array<T, N>saklayan bir örnek , bu yüzden değişmez.NTsize() == N

Dizi, N türleri dönüştürülebilen öğelere kadar listeyle başlatılabilen bir toplamdır T.

Bir dizi, bir kabın ve geri dönüşümlü bir kabın tüm gereksinimlerini karşılar ([container.requirements] ) , ancak varsayılan olarak oluşturulmuş bir dizi nesnesi boş değildir ve takasın sürekli karmaşıklığı yoktur. Bir dizi, bir dizi kapsayıcısının bazı gereksinimlerini karşılar. Açıklamalar burada yalnızca bu tablolardan birinde açıklanmayan dizi işlemleri ve ek anlam bilgisinin olduğu işlemler için sağlanır.

Standart aslında std::arraytam olarak bir tür kamu veri üyesine sahip olmayı gerektirmez T[N], bu nedenle teoridesizeof(To) != sizeof(From) veya is_­trivially_­copyable_­v<To>.

Yine de bu pratikte işe yaramazsa şaşıracağım.


2

Evet.

Her iki türün de aynı boyutta olduğu ve önemsiz bir şekilde kopyalanabildiği ölçüde , davranışını ve önerilen uygulamasını açıklayan makaleye göre , döküm başarılı olmalıdır.std::bit_cast

Basitleştirilmiş bir uygulaması aşağıdaki std::bit_castgibi olmalıdır:

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;
}

Bir şamandıra (4 bayt) ve bir dizi yana unsigned charolan size_of(float)ilgili tüm bu ileri sürerken, altta yatan std::memcpygerçekleştirilecektir. Bu nedenle, sonuçtaki dizideki her eleman şamandıranın bir ardışık baytı olacaktır.

Bu davranışı ispatlamak için, Compiler Explorer'da burada deneyebileceğiniz küçük bir örnek yazdım: https://godbolt.org/z/4G21zS . Şamandıra 5.0, Big Endian'dakiOx40a00000 şamandıra numarasının onaltılık gösterimine karşılık gelen bir bayt dizisi ( ) olarak düzgün bir şekilde saklanır .


std::arrayDolgu bitleri vb. Olmadığından emin misiniz ?
LF

1
Ne yazık ki, bazı kodların çalıştığı gerçeği içinde hiçbir UB anlamına gelmez. Örneğin auto bits = reinterpret_cast<std::array<unsigned char, sizeof(float)>&>(f), aynı çıktıyı yazabilir ve alabiliriz. Bir şey kanıtlıyor mu?
Evg

Şartnamesine uygun @LF: std::arraytatmin gereklerini ContiguiosContainer (17 C ++ beri) .
Manuel Gil

1
@ManuelGil: std::vectoraynı kriterleri de karşılar ve burada kullanılamaz. std::arraySınıf içindeki öğeleri (bir alanda) tutmayı ve iç diziye basit bir işaretçi olmasını engellemeyi gerektiren bir şey var mı ? (dizide bir alanda olması gerekmeyen bir boyutta olan vektörde olduğu gibi)
firda

@firda Toplam gereksinim std::array, öğelerin içeride saklanmasını etkili bir şekilde gerektirir, ancak düzen sorunları hakkında endişeleniyorum.
LF
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.