2013-11-21 3 views
6

Я реализует контейнер с прокси-итератор/ссылочного типа похожи на std::vector<bool> и столкновение в следующем номере, который я исхожу иллюстрировать с std::vector<bool> (этот вопрос не о std::vector<bool> !):Контейнер с прокси-итератора/ссылки и авто

#include <vector> 
#include <type_traits> 
int main() { 
    using namespace std; 
    vector<bool> vec = {true, false, true, false}; 
    auto value = vec[2]; // expect: "vector<bool>::value_type" 
    const auto& reference = vec[2]; // expect: "vector<bool>::const_reference" 

    static_assert(is_same<decltype(value), vector<bool>::value_type>::value, 
       "fails: type is vector<bool>::reference!"); 
    static_assert(is_same<decltype(reference), 
         vector<bool>::const_reference>::value, 
       "fails: type is const vector<bool>::reference&!"); 

    /// Consequence: 
    auto other_value = value; 
    other_value = false; 
    assert(vec[2] == true && "fails: assignment modified the vector"); 
  • есть ли способ, чтобы реализовать тип прокси, например, что оба прохода статической проверки в?

  • Есть ли какие-либо рекомендации по устранению этой проблемы при реализации такого контейнера?

Может быть с помощью оператора преобразования в auto/auto&/auto&&/const auto...?

EDIT: переработан пример, чтобы сделать его более понятным. Спасибо @LucDanton за его комментарий ниже.

+0

Вы знаете, что ['std :: vector '] (http://en.cppreference.com/w/cpp/container/vector_bool) - это специализация, которая не работает совсем как любой другой ['std :: VECTOR'] (http://en.cppreference.com/w/cpp/container/vector)? –

+0

@JoachimPileborg yes, 'std :: vector ' не является контейнером std. Он очень хорошо иллюстрирует использование прокси-итераторов/ссылочных типов и их ловушек. Я в основном спрашиваю, есть ли способ избежать их. – gnzlbg

+0

Вы также должны добавить тег [C++], см. [Страницу с информацией о тегах C++ 11] (http://stackoverflow.com/tags/c%2b%2b11/info) - но вам пришлось тогда удалите один из других тегов. – dyp

ответ

3

Как хорошо известно, имеет vector<bool>не-общий интерфейс по сравнению с первичным шаблоном vector<T>.

Релевантном различия в том, что вложенные типы reference и const_reference являются в typedef для T& и T const& в общем случае, и к прокси-классаreference и типа значениеbool для vector<bool>.

При доступе к векторных элементов, также важно помнить, что константность векторного объекта определяет, является ли reference или const_reference возвращается на operator[]. Кроме того, auto опустит контрольные квалификаторы, тогда как decltype сохранит их.

Давайте посмотрим на неконстантного/Const вектора 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"; 
} 

Live Example

Заметим, что для неконстантных vector<bool>, используя auto на operator[] даст вам ссылочный прокси, который не имеет семантику значений. Использование const vector<bool> позволит избежать этого. Я не понимаю, как это можно решить любым другим способом.

auto const& поведенческий эквивалентен но имеет is_convertible, а не is_same внутри static_assert. Я думаю, что это лучшее, что можно сделать.

Обратите внимание, что для общей итерации и алгоритмов STL на контейнерах-посредниках все не так мрачно. См. Hinnant's column on this.

+0

проблема заключается в том, что 'auto value' имеет тип' vector :: reverence' вместо 'vector :: value_type'. Только 'auto & reference' должен иметь тип' vector :: reference', но прямо сейчас он имеет тип 'vector :: reference &', что также неверно. – gnzlbg

+0

@gnzlbg да, и это неизбежно, потому что 'auto' следует правилам вывода аргументов шаблона, который, в свою очередь, не рассматривает последовательности преобразования (такие как вектор vector :: reference' to' bool', определяемый пользователем). Но это также не имеет большого значения, так как вы можете смягчить свое утверждение только для проверки конвертируемости типа, а не для равенства типов. Вот что такое прокси-сервер: использовать что-то еще за спиной, не мешая вам. – TemplateRex

+1

"auto v = vec [1]; v = other;" не изменяет значение контейнера *, если вы не получаете прокси-сервер. В этом случае он изменяет значение в контейнере! Так что да, тот факт, что auto следует за TAD, поражает цель использования прокси-сервера, потому что он мешает вам не очевидным образом. Кроме того, пользователь * действительно * должен знать, что он получает прокси-сервер, чтобы избежать этой ловушки. – gnzlbg

3

Прокси и auto плохо взаимодействуют, именно потому, что auto раскрывает информацию о типах, которые должны были скрываться.

Половина запросов для интереса для operator auto-стилевых вещей (в основном, «при выводе меня как типа, используйте этот тип вместо»), но AFAIK ни один из них даже не сделал это для официального предложения.

Другая проблема заключается в том, что vector<bool> является неожиданным, потому что это только конкретизацией vector, который использует прокси. Были и другие предварительные предложения, призывающие к тому, чтобы vector<bool> были устаревшими и в конечном итоге возвращались к тому, чтобы быть неспецифичными, а класс специального назначения - bitvector.

+1

Я пишу контейнер, который должен вернуть прокси. Есть ли что-нибудь, что я могу сделать, чтобы спасти моих пользователей от этих подводных камней? – gnzlbg

+2

№ Прокси не полностью прозрачны, а 'auto' делает их еще меньше, чем раньше. Это слабость в языке. –

Смежные вопросы