태그 보관물: stl

stl

vector <bool>이 STL 컨테이너가 아닌 이유는 무엇입니까? 개선하는 50 가지

Scott Meyers의 저서 Effective STL : 표준 템플릿 라이브러리 사용을 개선하는 50 가지 특정 방법 의 항목 18은 vector <bool>STL 컨테이너가 아니고 실제로 bools를 보유하지 않기 때문에 피해야한다고 말합니다 .

다음 코드 :

vector <bool> v;
bool *pb =&v[0];

컴파일되지 않고 STL 컨테이너의 요구 사항을 위반합니다.

오류:

cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization

vector<T>::operator []반환 유형은이어야 T&하지만 왜 특별한 경우 vector<bool>입니까?

vector<bool>실제로 무엇 으로 구성됩니까?

항목은 추가로 말합니다.

deque<bool> v; // is a STL container and it really contains bools

이것의 대안으로 사용할 수 있습니까 vector<bool>?

누구든지 이것을 설명해 주시겠습니까?



답변

공간 최적화를 위해 C ++ 표준 (C ++ 98까지 거슬러 vector<bool>올라감)은 각 부울이 일반 부울처럼 1 바이트가 아닌 1 비트의 공간 만 사용하는 특수 표준 컨테이너를 명시 적으로 호출합니다 (일종의 부울 구현). “동적 비트 셋”). 이 최적화의 대가로 일반 표준 컨테이너의 모든 기능과 인터페이스를 제공하지는 않습니다.

이 경우 바이트 내에서 비트 주소를 가져올 수 없기 때문에 a를 operator[]반환 할 수 bool&없지만 대신 해당 특정 비트를 조작 할 수있는 프록시 객체를 반환합니다. 이 프록시 객체는가 아니기 때문에 “일반”컨테이너에 대한 이러한 연산자 호출의 결과로 할 수 bool&있는 bool*것과 같은 주소를 할당 할 수 없습니다 . 이는 bool *pb =&v[0];유효한 코드가 아님을 의미합니다 .

반면에 deque이러한 전문화는 호출되지 않으므로 각 부울은 바이트를 취하고에서 반환되는 값의 주소를 가져올 수 있습니다 operator[].

마지막으로 MS 표준 라이브러리 구현은 deques에 작은 청크 크기를 사용한다는 점에서 (논문의 여지가 있지만) 차선책입니다. 즉, deque를 대체물로 사용하는 것이 항상 올바른 답은 아닙니다.


답변

vector<bool>값에 1 비트 만 사용하는 압축 된 형식의 부울 값을 포함합니다 (bool [] 배열이 수행하는 방식은 8이 아님). C ++에서는 비트에 대한 참조를 반환 할 수 없으므로 메모리의 일부 비트에 대한 인터페이스를 제공하고 표준 연산자 및 캐스트를 사용할 수있는 특수 도우미 유형 인 “비트 참조”가 있습니다.


답변

문제는 실제 참조 대신 프록시 참조 객체vector<bool>반환하여 C ++ 98 스타일 코드 가 컴파일되지 않는다는 것입니다. 그러나, 현대 C ++ 11이 경우 컴파일 할 수 도 프록시 포인터 객체를 반환한다 . Howard Hinnant는 이러한 프록시 참조 및 포인터를 사용할 때 알고리즘 개선 사항을 자세히 설명 하는 블로그 게시물을 작성했습니다.bool * p = &v[0];auto p = &v[0];operator&

Scott Meyers는 프록시 클래스에 대한 더 효과적인 C ++ 의 긴 항목 30을 가지고 있습니다. 당신은에 먼 길을 올 수있는 거의 주어진 유형 : 모방 내장 유형을 T하는 프록시의 쌍 (예 reference_proxy<T>와는 iterator_proxy<T>) 의미에서 서로 일치 할 수 reference_proxy<T>::operator&()iterator_proxy<T>::operator*()서로의 역이다.

그러나 어떤 시점에서 프록시 개체를 다시 매핑하여 T*또는 처럼 동작해야합니다 T&. 반복기 프록시의 경우 모든 기능을 다시 구현하지 않고도 operator->()템플릿 T의 인터페이스를 오버로드 하고 액세스 할 수 있습니다 . 그러나 참조 프록시의 경우 오버로드가 필요 operator.()하며 현재 C ++에서는 허용되지 않습니다 (Sebastian Redl이 BoostCon 2013에서 이러한 제안제시 했지만 ). .get()참조 프록시 내부의 멤버 처럼 자세한 해결 방법을 만들 거나 참조 내부의 모든 T인터페이스를 구현할 수 있습니다 (이는vector<bool>::bit_reference), 그러나 이것은 내장 구문을 잃거나 유형 변환에 대한 내장 의미가없는 사용자 정의 변환을 도입합니다 (인수 당 최대 하나의 사용자 정의 변환을 가질 수 있음).

TL; DR : vector<bool>표준은 실제 참조가 필요하기 때문에 컨테이너가 아니지만 거의 컨테이너처럼 동작하도록 만들 수 있습니다. 적어도 C ++ 98에서보다 C ++ 11 (자동)에 훨씬 더 가깝습니다.


답변

많은 사람들이 vector<bool>전문화가 실수라고 생각합니다 .

“Deprecating Vestigial Library Parts in C ++ 17” 논문 에서 벡터 부분 전문화
재고 하기위한 제안이 있습니다
.

std :: vector의 bool 부분 전문화가 컨테이너 요구 사항을 충족하지 못하는 오랜 역사가 있었고, 특히 임의 액세스 반복기의 요구 사항을 충족하지 않는 반복기가있었습니다. 이 컨테이너를 폐기하려는 이전 시도가 C ++ 11, N2204에 대해 거부되었습니다 .


거부 이유 중 하나는 템플릿의 특정 전문화를 폐기한다는 것이 무엇을 의미하는지 명확하지 않기 때문입니다. 이는 신중한 표현으로 해결할 수 있습니다. 더 큰 문제는 벡터의 (포장 된) 전문화가 표준 라이브러리의 클라이언트가 진정으로 추구하지만 더 이상 사용할 수없는 중요한 최적화를 제공한다는 것입니다. N2050 과 같은 대체 시설이 제안되고 승인 될 때까지 표준의이 부분을 더 이상 사용하지 않을 수 있습니다 . 안타깝게도 현재 도서관 진화 워킹 그룹에 제안 된 수정 된 제안은 없습니다.


답변

구현 방법을 살펴보십시오. STL은 템플릿을 기반으로 크게 빌드되므로 헤더에 코드가 포함되어 있습니다.

예를 들어 여기 에서 stdc ++ 구현을 보십시오 .

stl 준수 비트 벡터가 아니지만 여기 에서 llvm :: BitVector 는 것도 흥미 롭습니다 .

의 핵심은 몇 가지 제한 사항 이있는 것과 유사하게 동작 하도록 llvm::BitVector하는 중첩 된 클래스 reference와 적절한 연산자 오버로딩 입니다. 아래 코드는 BitVector가 각 값에 대해 1 바이트를 사용하지 않고 실제 구현이 bool의 실제 배열처럼 동작하도록 호출 된 클래스를 숨기는 방법을 보여주는 단순화 된 인터페이스 입니다.BitVectorvectorreference

class BitVector {
public:
  class reference {
    reference &operator=(reference t);
    reference& operator=(bool t);
    operator bool() const;
  };
  reference operator[](unsigned Idx);
  bool operator[](unsigned Idx) const;
};

이 코드에는 멋진 속성이 있습니다.

BitVector b(10, false); // size 10, default false
BitVector::reference &x = b[5]; // that's what really happens
bool y = b[5]; // implicitly converted to bool 
assert(b[5] == false); // converted to bool
assert(b[6] == b[7]); // bool operator==(const reference &, const reference &);
b[5] = true; // assignment on reference
assert(b[5] == true); // and actually it does work.

이 코드에는 실제로 결함이 있습니다. 실행 해보십시오.

std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator

assert( (&b[5] - &b[3]) == (5 - 3) );실패 하기 때문에 작동하지 않을 것 입니다.llvm::BitVector )

이것은 매우 간단한 llvm 버전입니다. std::vector<bool>또한 작동하는 반복자가 있습니다. 따라서 호출 for(auto i = b.begin(), e = b.end(); i != e; ++i)이 작동합니다. 그리고 또한std::vector<bool>::const_iterator .

그러나 일부 경우에는 std::vector<bool>다르게 작동하는 한계가 있습니다 .


답변

이것은 http://www.cplusplus.com/reference/vector/vector-bool/ 에서 가져온 것입니다 .

bool의 벡터 이것은 bool 유형의 요소에 사용되며 공간에 최적화 된 벡터의 특수 버전입니다.

다음과 같이 변경된 벡터의 특수화되지 않은 버전처럼 작동합니다.

  • 저장소는 반드시 부울 값의 배열 일 필요는 없지만 라이브러리 구현은 각 값이
    단일 비트에 저장되도록 저장소를 최적화 할 수 있습니다 .
  • 요소는 할당 자 개체를 사용하여 구성되지 않지만 해당 값은 내부 저장소의 적절한 비트에 직접 설정됩니다.
  • 멤버 함수 플립 및 멤버 교체를위한 새 서명.
  • bool 참조 를 에뮬레이트 하는 인터페이스를 사용하여 컨테이너의 내부 저장소에있는 개별 비트에 액세스하는 클래스 인 특수 멤버 유형 참조입니다. 반대로 멤버 유형 const_reference는 일반 부울입니다.
  • 컨테이너가 사용하는 포인터 및 반복기 유형은
    대부분의 예상 동작을 시뮬레이션 하지만 반드시 포인터도 일치하는 반복자도 아닙니다 .

이러한 변경 사항은이 전문화에 기발한 인터페이스를 제공하고 처리보다 메모리 최적화를 선호합니다 (사용자의 요구에 맞거나 맞지 않을 수 있음). 어떤 경우에도 bool에 대한 특수화되지 않은 벡터 템플릿을 직접 인스턴스화 할 수 없습니다. 다른 유형 (char, unsigned char) 또는 컨테이너 (예 : deque)를 사용하여 래퍼 유형을 사용하거나 특정 할당 자 유형에 대해 더 전문화하는 것에서이 범위를 피하는 해결 방법입니다.

bitset은 고정 크기 비트 배열에 유사한 기능을 제공하는 클래스입니다.


답변