Reputation: 7549
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
, i.e., 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
.
Upvotes: 0
Views: 150
Reputation: 117871
Is it possible to instead have a (
const
or non-const
) referenceiter
, such that*iter
is aconst 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&
.
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 aconst 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&`
}
Upvotes: 1
Reputation: 9008
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.
Upvotes: 1