std :: array를 사용하는 std :: bit_cast


14

그의 최근 강연에서 "현대 C의 유형 말장난 ++" 티무르 Doumler는 말했다std::bit_cast비트가 캐스팅에 사용할 수 없습니다 floatunsigned char[4]C 스타일 배열은 함수에서 반환 할 수 없기 때문에. 우리는 std::memcpy비슷한 것을 reinterpret_cast<unsigned char*>(&f)[i]잘 정의 할 때 C ++ 23 (또는 그 이후 버전)을 사용 하거나 기다려야 합니다.

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들어 char[]갑니다 (일반적으로 a 는 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]; };그렇게 문자가 집합의 요소가 될 필요가 있지만,이 그들 중 하나를 직접 집계 요소가 될 수 있습니다 - 또는 단일 하위 집합의 요소.
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;
}

모든 주장 unsigned charsize_of(float)관련 하여 float (4 바이트) 및 배열이므로 기본 std::memcpy이 수행됩니다. 따라서 결과 배열의 각 요소는 float의 연속 된 1 바이트입니다.

이 동작을 증명하기 위해 컴파일러 탐색기에서 https://godbolt.org/z/4G21zS에서 시도 할 수있는 작은 예제를 작성했습니다 . float 5.0은 Big EndianOx40a00000 에서 해당 float 숫자의 16 진수 표현에 해당하는 바이트 배열 ( ) 로 올바르게 저장됩니다 .


std::array패딩 비트 등이없는 것이 확실 합니까?
LF

1
불행히도 일부 코드가 작동한다는 사실은 UB가 없다는 것을 의미하지는 않습니다. 예를 들어, auto bits = reinterpret_cast<std::array<unsigned char, sizeof(float)>&>(f)정확히 같은 결과를 작성 하고 얻을 수 있습니다 . 그것은 무엇을 증명합니까?
Evg

사양에 따라 @LF : ContiguiosContainer (C ++ 17부터)std::array 요구 사항을 충족합니다 .
Manuel Gil

1
@ManuelGil : std::vector또한 동일한 기준을 만족하므로 여기서는 사용할 수 없습니다. std::array클래스 내부의 요소를 필드에서 보유하여 내부 배열에 대한 간단한 포인터가되는 것을 방지 해야하는 것이 있습니까? (벡터에서와 같이 크기를 가지며 배열이 필드에 필요하지 않음)
firda

@firda std::array효과적으로 집계 하려면 요소를 내부에 저장해야하지만 레이아웃 문제가 걱정됩니다.
LF
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.