3

Why does range-for over const vector<Thing> yield const Thing ?

I thought fn_a would compile fine :

#include <vector>

class Some {
    public:
      void not_const();
};


void fn_a ( const std::vector<Some> &vs) {
     for( Some &s : vs )
        s.not_const();

}

void fn_b (  std::vector<Some const> &vs) {
     for( Some &s : vs )
        s.not_const();
}

Errors ( omitting some others ):

a.cc:10:21: error: binding reference of type ‘Some&’ to ‘const Some’ discards qualifiers
a.cc:16:21: error: binding reference of type ‘Some&’ to ‘const Some’ discards qualifiers

Q: Is it possible to range-for over a const vector and get mutable elements ?

vrdhn
  • 4,024
  • 3
  • 31
  • 39
  • Make copies of the elements in the `for` statement and you can mutate them all you want. Why would you expect a `const vector` to allow you to mutate its contents? – Praetorian Feb 14 '18 at 04:56

1 Answers1

2

Why does range-for over const vector<Thing> yield const Thing ?

A range-for loop uses an iterator to iterate through the vector. As such, it will call the vector's begin() method to get the iterator. Calling begin() on a const vector yields a const_iterator, which returns a const Thing& when dereferenced.

Is it possible to range-for over a const vector and get mutable elements ?

Not safely. You could const_cast away the const from the reference returned from dereferencing the const_iterator, but that is undefined behavior, especially in your fn_b example where the vector's items are themselves const to begin with.

If you want to work with mutable items, you need to work with a mutable vector.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • yeah , but i am wondering about the rationale behind the philosphy of applying the constness of container , to it's contents. I wouldn't want a function to modify the vector itself ( to avoid invalidation of other iterators ), but would want to modify the content ( first level, or somewhere deep down ) ! – vrdhn Feb 14 '18 at 05:07
  • @VardhanVarma See [What is a "span" and when should I use one?](https://stackoverflow.com/q/45723819/1896169) – Justin Feb 14 '18 at 05:37
  • @Justin what does that have to do with this issue? – Remy Lebeau Feb 14 '18 at 15:31
  • @RemyLebeau vrdhn asked about modifying the content, but not the vector. In that case, a mutable span does exactly what they want – Justin Feb 14 '18 at 17:11
  • @Justin: AFAIK, you can't mutate items via a span of an immutable container. Look at Microsoft's [`gsl::span`](https://github.com/Microsoft/GSL/blob/master/include/gsl/span) (since `std::span` is not available yet). Constructing a `gsl::span` from a `const` vector calls `gsl::span(const Container&)`, which only accepts `const T` as the span's `ElementType` and requires `Container::pointer` to be convertible to `const T*`. As such, the span's `operator[]`, `operator()`, and `at()` members will return `const T&`, the `data()` member will return `const T*`, and iterators will return `const T&` – Remy Lebeau Feb 14 '18 at 17:31
  • @RemyLebeau yes, but the idea was to make a span over the non-const vector. Then the "vector parts" can't be mutated, but the contained elements are still mutable – Justin Feb 14 '18 at 18:07
  • @Justin: I know what the *idea* is, but based on the *implementation*, that is simply not possible with a span. If the vector itself is `const`, the span will provide `const` access to the vector's items, even if the items are not themselves `const`. Only a span of a non-`const` vector will allow non-`const` access to the vector items – Remy Lebeau Feb 14 '18 at 20:48