简体   繁体   中英

reference with const access

Using pointers, I can create all combinations of (const/non_const) to (T / const T) for any type T, as discussed in detail this answer .

But using references, how can I define a reference to a variable which can be dereferences (like an iterator), such that the dereferenced reference gives a const access?

For example, consider this program:

#include <array>

int main()
{
    std::array<int, 3> a{0, 0, 0};
    auto const& iter = a.begin();
    // iter++; // Error!
    (*iter)++;
}

iter is const variable, and if you uncomment the line iter++ the code cannot compile. But iter dereferences to a non-const int, which in fact can be incremented with (*iter)++ . In a sense, iter mimics int * const , ie, a const pointer to int.

Is it possible to instead have a (const or non_const) reference iter , such that *iter is a const int?

Please bear in mind that I am aware that I could use const auto& iter = a.cbegin() in the example above. The purpose of the question is how to define iter , such that, when dereferenced, give a const access, irrespective of what is on the right of the equal operator. For iterators I can imagine to explicitly bind to a const_iterator , like:

const decltype(a)::const_iterator& iter = a.begin();

This solution works because I know that the equivalent "const-access" of an iterator is a const_iterator .

But, more generally:

(some type)& iter = rhs

If all I know is that *rhs is a int, is it possible to find out the type of iter, such that *iter is a const int?

Another point worth to consider is the possible cost of the conversion of rhs .

The purpose of the question is how to define iter , such that, when dereferenced, give a const access

And this is where const iterators come into play. For iterators it's actually a less global question, because a const iterator merely means a standard iterator to a constant container:

const std::array<int, 3> a{ 0, 0, 0 };
static_assert(
    std::is_same_v<decltype(a.begin()), decltype(a.cbegin())>,
    "The iterators are of different type"
);

Constness of the iterators' own types in this case doesn't actually depend on whether they point to a const type or not. As you already noticed, const iterator instead means that the iterator itself cannot be changed and point to a different object.

Answering your second question:

Is it possible to instead have a (const or non_const) reference to const int?

No, a reference is always const implicitly and cannot be rebound after being bound to a variable.

Is it possible to instead have a ( const or non- const ) reference iter , such that *iter is a const int ?

You call the std::array::cbegin() member function that will return a std::array<int, 3>::const_iterator . Dereferencing that will give you a const int& .

  • You can however not take the const_iterator by a non- const reference. You must take it by value or by const& (to prolong the life of the temporary returned by the cbegin() function). The general recommendation is to take iterators by value though.

is it possible to find out the type of iter , such that *iter is a const int ?

If you know that iter is some kind of pointer ( const T* , const T*& , T* or T*& etc.) then you could get a const T* like this:

const std::remove_cvref_t<decltype(iter)> constiter = iter;

For the general case, getting a const_iterator from an iterator without using the container's typedef for const_iterator , will be a bit cumbersome. One way could be to wrap the iterator in an interator class that only returns const& s when dereferenced. Here's an example with a std::list<int>::iterator which is a little more complicated than a pointer. It works for pointers too though.

#include <iostream>
#include <list>

template<class It>
struct const_iterator {
    using IterType = std::remove_reference_t<It>;
    using Traits = std::iterator_traits<IterType>;   

    using difference_type = typename Traits::difference_type;
    using value_type = const typename Traits::value_type;
    //                 ^^^^^
    using pointer = value_type*;
    using reference = value_type&;
    using iterator_category = typename Traits::iterator_category;

    const_iterator(It nc) : value(nc) {}
    
    // dereferencing gives a const&
    reference operator*() const { return *value; }

    bool operator==(const const_iterator& rhs) const { return value == rhs.value; }
    bool operator!=(const const_iterator& rhs) const { return !(*this == rhs); }
    
    const_iterator& operator++() { ++value; return *this; }
    const_iterator operator++(int) { const_iterator rv(*this); ++value; return rv; }

    // Add conditional support for proxy member functions supported
    // by the original iterator type / iterator_category

    IterType value;
};

template<class It>
const_iterator(It) -> const_iterator<It>;

int main()
{
    std::list<int> a{1, 2, 3};
    auto iter = a.begin();
    *iter = 10;                // Ok

    const_iterator cit(iter);  // make a const_iterator from the original iterator
    ++cit;                     // Ok
    std::cout << *cit << '\n'; // prints 2
    //*cit = 20;               // Error: *cit is a `const int&`
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM