2011-12-06 49 views
46
#include <vector> 

struct A 
{ 
    void foo(){} 
}; 

template< typename T > 
void callIfToggled(bool v1, bool &v2, T & t) 
{ 
    if (v1 != v2) 
    { 
     v2 = v1; 
     t.foo(); 
    } 
} 

int main() 
{ 
    std::vector<bool> v= { false, true, false }; 

    const bool f = false; 
    A a; 

    callIfToggled(f, v[0], a); 
    callIfToggled(f, v[1], a); 
    callIfToggled(f, v[2], a); 
} 

Việc biên soạn các ví dụ trên tạo ra lỗi tiếp theo:Tại sao vector <bool> :: tham chiếu không trả về tham chiếu đến bool?

dk2.cpp: In function 'int main()': 
dk2.cpp:29:28: error: no matching function for call to 'callIfToggled(const bool&, std::vector<bool>::reference, A&)' 
dk2.cpp:29:28: note: candidate is: 
dk2.cpp:13:6: note: template<class T> void callIfToggled(bool, bool&, T&) 

tôi biên soạn sử dụng g ++ (phiên bản 4.6.1) như thế này:

g++ -O3 -std=c++0x -Wall -Wextra -pedantic dk2.cpp 

Câu hỏi đặt ra là tại sao điều này xảy ra? Có phải vector<bool>::reference không phải là bool&? Hay là lỗi của trình biên dịch?
Hoặc, tôi đang thử điều gì đó ngu ngốc? :)

+12

Thật không may, mặc dù tên của nó, 'std :: vector ' không phải là 'vectơ' của' bool'. –

+1

Như một giải pháp thay thế, bạn có thể sử dụng 'std :: unique_ptr (new bool [3])' ... –

+2

[Khi nào một Container không phải là một Container?] (Http://www.gotw.ca/publications) /mill09.htm) chỉ là về vấn đề này. – legends2k

Trả lời

49

Vector is specialized for bool.

Nó được coi là sai lầm của tiêu chuẩn. Sử dụng vector<char> thay vì:

template<typename t> 
struct foo { 
    using type = t; 
}; 
template<> 
struct foo<bool> { 
    using type = char; 
}; 

template<typename t, typename... p> 
using fixed_vector = std::vector<typename foo<t>::type, p...>; 

Đôi khi bạn có thể cần tham chiếu đến một bool chứa bên trong vector. Thật không may, việc sử dụng vector<char> chỉ có thể cung cấp cho bạn các tham chiếu đến ký tự. Nếu bạn thực sự cần bool&, hãy xem Boost Containers library. Nó có phiên bản không chuyên biệt của vector<bool>.

+3

Giải pháp tuyệt vời và bản trình diễn đẹp mắt của cú pháp 'sử dụng' mới để tạo răng cưa. –

+5

+1 cho sử dụng * thông minh * của 'sử dụng' (không có ý định chơi chữ). – Nawaz

+0

@Nawaz Tôi thề với Chúa rằng chơi chữ được dự định. –

15

std::vector<bool> là một vùng chứa không phù hợp. Để tối ưu hóa dung lượng, gói này có kích thước bool và không thể cung cấp tham chiếu.

Sử dụng boost::dynamic_bitset thay thế.

+0

Để có được một loại tham chiếu, bạn cần sử dụng toán tử [] (kết quả là dynamic_bitset :: reference). Không có trình lặp nào. – reder

+2

-1 vì không đề cập đến cách 'dynamic_bitset' khác hoặc tốt hơn. Tất nhiên nó cũng không thể trả về 'bool &'. – Potatoswatter

44

Kỳ vọng của bạn là bình thường, nhưng vấn đề ở đây là std::vector<bool> là một loại thử nghiệm bởi ủy nhiệm C++. Nó thực sự là một chuyên môn mẫu lưu trữ các giá trị bool được đóng gói chặt chẽ trong bộ nhớ: một bit cho mỗi giá trị.

Và vì bạn không thể tham chiếu một chút, có vấn đề của bạn.

+7

+1. std :: vector chỉ hỗ trợ một tập con của chức năng được cung cấp bởi std :: vector. It's xấu xí vì bạn phải thay thế bool bằng một kiểu khác (char) để có được một vector làm việc. – josefx

+1

Một thử nghiệm nhất (nếu không phải tất cả) thành viên ủy ban hối hận! – curiousguy

5

Chỉ cần tôi 2 xu:

std::vector<bool>::reference là một typedef cho struct _Bit_reference được định nghĩa là

typedef unsigned long _Bit_type; 

struct _Bit_reference 
    { 
    _Bit_type * _M_p; 
    _Bit_type _M_mask; 

    // constructors, operators, etc... 

    operator bool() const 
    { return !!(*_M_p & _M_mask); } 
    }; 

Thay đổi chức năng như thế này, nó hoạt động (tốt, biên dịch ít nhất, đã không kiểm tra) :

template< typename T > 
void callIfToggled(bool v1, std::vector<bool>::reference v2, T & t) 
{ 
    bool b = v2; 
    if (v1 != b) 
    { 
     v2 = v1; 
     t.foo(); 
    } 
} 

CHỈNH SỬA: Tôi đã thay đổi điều kiện từ (v1! = V2), ý kiến ​​hay là (v1! = B).

+1

Nó hoạt động, nhưng đây có phải là phần mở rộng của g ++ không? –

+1

Nó không phải là một phần mở rộng, nó chỉ là cách GCC thực hiện các vector chuyên môn. Tôi không biết tiêu chuẩn nói gì về điều này. Bạn có thể xem nó cho chính mình: std_bvector.h trong lib/gcc/mingw32/4.6.1/include/C++/bits. (Cây thư mục của bạn có thể khác, nhưng có thể là tương tự) – jrok

+0

Chắc chắn, nó hoạt động, trong trường hợp cụ thể này - minh họa thêm tại sao 'vector ' là một từ sai lầm khủng khiếp - vì nó trở nên vô cùng khó khăn, tẻ nhạt hoặc không thể sử dụng các tình huống mã hoạt động với các thùng chứa _actual_ khác. Tuy nhiên, tôi nghĩ một giải pháp có thể sử dụng ở đây sẽ là sử dụng 'template void callIfToggled (B && v1, B && v2, T && t)' và dựa vào toán tử chuyển đổi từ 'v2' - mà tôi đoán là lý do để biết ơn để chuyển tiếp tài liệu tham khảo. Tuy nhiên, không cho phép lựa chọn sai tên! –

13

std::vector<bool> gói nội dung của nó sao cho mỗi giá trị Boolean được lưu trữ trong một bit, tám bit đến một byte. Đây là bộ nhớ hiệu quả nhưng tính toán chuyên sâu, vì bộ vi xử lý phải thực hiện số học để truy cập bit được yêu cầu. Và nó không hoạt động với tham chiếu bool hoặc ngữ nghĩa con trỏ, vì các bit trong một byte không có địa chỉ trong mô hình đối tượng C++.

Bạn vẫn có thể khai báo biến số std::vector<bool>::reference và sử dụng biến số đó như là bool&. Điều này cho phép các thuật toán chung tương thích.

std::vector<bool> bitvec(22); 
std::vector<bool>::reference third = bitvec[ 2 ]; 
third = true; // assign value to referenced bit 

Trong C++ 11, bạn có thể làm việc xung quanh này bằng auto&& specifier tự động lựa chọn một tài liệu tham khảo giá trị trái bị ràng buộc vào các yếu tố vector hoặc một tham chiếu rvalue ràng buộc với một tạm thời.

std::vector<bool> bitvec(22); 
auto &&third = bitvec[ 2 ]; // obtain a std::vector<bool>::reference 
third = true; // assign value to referenced bit 
+0

Câu trả lời hay, đặc biệt là đề cập đến '&&', điều rất quan trọng đối với mã chung để cho phép các loại proxy/trình lặp trở nên hữu ích. Tất nhiên, nó hoạt động tốt như nhau trong các vòng lặp: 'for (auto && it: bizarreContainer)' –

1

Thực hiện một cấu trúc với một bool trong nó, và làm cho vector<> sử dụng kiểu struct.

Hãy thử:

vector<struct sb> nơi sbstruct {boolean b];

sau đó bạn có thể nói

push_back({true})

làm

typedef struct sbool {bool b;} boolstruct; và sau đó vector<boolstruct> bs;

Các vấn đề liên quan