Как известно, vector<bool>
имеет нестандартный интерфейс по сравнению с основным шаблоном vector<T>
.
Соответствующие различия заключаются в том, что вложенные типы reference
и const_reference
являются typedef
для T&
и T const&
в общем случае, а для прокси-класса reference
и типа значения bool
для vector<bool>
.
При доступе к векторным элементам также важно помнить, что постоянство векторного объекта определяет, что operator[]
возвращает reference
или const_reference
. Кроме того, auto
отбрасывает ссылочные квалификаторы, а decltype
сохраняет их.
Давайте посмотрим на неконстантный/константный вектор bool
/int
и используем auto
, decltype(auto)
и auto const&
(обычный auto&
приведет к проблемам в реальном времени для прокси). Вы получаете следующее поведение:
#include <vector>
#include <type_traits>
#include <typeinfo>
#include <iostream>
#include <ios>
int main() {
using namespace std;
vector<bool> vb = { true, false, true, false };
vector<int > vi = { 1, 0, 1, 0 };
auto vb2 = vb[2]; // vector<bool>::reference != bool
auto vi2 = vi[2]; // int
decltype(auto) rvb2 = vb[2]; // vector<bool>::reference
decltype(auto) rvi2 = vi[2]; // int&
auto const& crvb2 = vb[2]; // vector<bool>::reference const& != bool const&
auto const& crvi2 = vi[2]; // int const&
auto ovb2 = vb2;
ovb2 = false; // OOPS ovb2 has reference semantics
cout << boolalpha << (vb[2] == true) << "\n";
auto ovi2 = vi2;
ovi2 = 0; // OK, ovi2 has value semantics
cout << boolalpha << (vi[2] == 1) << "\n";
static_assert(is_convertible<decltype(vb2), vector<bool>::value_type>::value, "");
static_assert(is_same <decltype(vi2), vector<int >::value_type>::value, "");
static_assert(is_same <decltype(rvb2), vector<bool>::reference>::value, "");
static_assert(is_same <decltype(rvi2), vector<int >::reference>::value, "");
static_assert(is_convertible<decltype(crvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(crvi2), vector<int >::const_reference>::value, "");
vector<bool> const cvb = { true, false, true, false };
vector<int > const cvi = { 1, 0, 1, 0 };
auto cvb2 = cvb[2]; // vector<bool>::const_reference == bool
auto cvi2 = cvi[2]; // int
decltype(auto) rcvb2 = cvb[2]; // vector<bool>::const_reference == bool
decltype(auto) rcvi2 = cvi[2]; // int const&
auto const& crcvb2 = cvb[2]; // vector<bool>::reference const& != bool const&
auto const& crcvi2 = cvi[2]; // int const&
static_assert(is_same <decltype(cvb2), vector<bool>::value_type>::value, "");
static_assert(is_same <decltype(cvi2), vector<int >::value_type>::value, "");
static_assert(is_same <decltype(rcvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(rcvi2), vector<int >::const_reference>::value, "");
static_assert(is_convertible<decltype(crcvb2), vector<bool>::const_reference>::value, "");
static_assert(is_same <decltype(crcvi2), vector<int >::const_reference>::value, "");
auto ocvb2 = cvb2;
ocvb2 = false; // OK, ocvb2 has value semantics
cout << boolalpha << (cvb[2] == true) << "\n";
auto ocvi2 = cvi2;
ocvi2 = 0; // OK, ocvi2 has value semantics
cout << boolalpha << (cvi[2] == 1) << "\n";
}
Живой пример
Обратите внимание, что для неконстантного vector<bool>
использование auto
на operator[]
даст вам эталонный прокси, который не имеет семантики значений. Использование const vector<bool>
позволит избежать этого. Я не вижу, как это можно решить по-другому.
auto const&
поведенчески эквивалентен, но имеет is_convertible
, а не is_same
внутри static_assert
. Я думаю, что это лучшее, что можно сделать.
Обратите внимание, что для общей итерации и алгоритмов STL на прокси-контейнерах все не так безрадостно. См. колонку Хиннанта по этому поводу.
person
TemplateRex
schedule
22.11.2013
std::vector<bool>
работает не так, как любая другойstd::vector
? - person Some programmer dude   schedule 21.11.2013std::vector<bool>
не является стандартным контейнером. Он очень четко иллюстрирует использование прокси-итераторов/ссылочных типов и их подводные камни. Я в основном спрашиваю, есть ли способ избежать их. - person gnzlbg   schedule 21.11.2013decltype(auto) var = init;
. Это похоже наauto&& var = init;
? - person aschepler   schedule 21.11.2013decltype((bool)value)
будет иметь проход static_assert дляclang
, хотя я сомневаюсь, что это имеет какое-либо отношение к вопросу. - person   schedule 21.11.2013static_assert
проходным. Вопрос в том, есть ли способ заставить его пройти без явного преобразования. Пользователю не нужно знать, что он получает не логическое значение, а тип прокси. - person gnzlbg   schedule 21.11.2013auto
иdecltype(auto)
совпадают. Если вы хотите, чтобы они различались, вы столкнетесь с сомнительными соображениями по поводу времени жизни (например, где будет находиться прокси-объект и как долго?). - person Luc Danton   schedule 21.11.2013auto
,auto&
,... Затем я контролирую, где находятся прокси-объекты, поэтому проблемы со сроком службы исчезают. - person gnzlbg   schedule 22.11.2013