Container with proxy iterator / link and auto

I am implementing a container with a proxy iterator / reference type with a similar to std::vector<bool> and am facing the following problem, which I go to for example std::vector<bool> (this question does not apply to 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"); 
  • Is there a way to implement a proxy type so that both static assert pass?

  • Are there any recommendations for resolving this issue when implementing such a container?

Perhaps using the conversion operator in auto / auto& / auto&& / const auto... ?

EDIT : Redesigned the example to make it clearer. Thanks to @LucDanton for his comment below.

+6
source share
2 answers

As you know, vector<bool> has no common interface compared to the main vector<T> template.

The corresponding differences are that the nested types reference and const_reference are typedef for T& and T const& in the general case, and for the proxy class reference , the bool value for vector<bool> .

When accessing vector elements, it is also important to remember that the constant of a vector object determines whether reference or const_reference using operator[] . In addition, auto will discard qualifiers, while decltype keep them.

Look at the non-const / const vector bool / int and use auto , decltype(auto) and auto const& (plain auto& will lead to real-time problems for the proxy), you get the following behavior:

 #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

Note that for a non-const vector<bool> using auto on operator[] will give you a reference proxy that has no semantics of values. Using const vector<bool> will avoid this. I do not understand how this can be solved in any other way.

auto const& is behaviorally equivalent, but has is_same and not is_same inside static_assert . I think this is the best you can do.

Please note that for general iteration and STL algorithms on intermediary containers, everything is not so gloomy. See the Hinnant column in this .

+3
source

Proxies and auto interact poorly, precisely because auto shows things about types that should have remained hidden.

In half the cases, there are some requests for interest in operator auto stylish things (basically, "when you select me as a type, use this type instead of"), but AFAIK none of them even got into the official offer.

Another problem is that vector<bool> is unexpected as it is the only instance of vector that uses proxies. There were other preliminary suggestions calling for vector<bool> become obsolete and eventually return to non-specific, with a special bitvector class introduced to replace it.

+3
source

Source: https://habr.com/ru/post/958574/


All Articles