61

Does const vector<A> mean that its elements are constas well?

In the code below,

v[0].set (1234); in void g ( const vector<A> & v ) produces the compiler error

const.cpp:28:3: error: member function 'set' not viable: 'this' argument has type 'const value_type' (aka 'const A'), but function is not marked const

Why?

But (*v[0]).set(1234); in void h ( const vector<A *> & v ) is OK for the compiler.

What's the difference between the versions?

// ...........................................................
class A {
private:
  int a;
public:
  A (int a_) : a (a_) { }
  int get () const { return a; }
  void set (int a_) { a = a_; }
};

// ...........................................................
void g ( const vector<A> & v ) {
  cout << v[0].get();
  v[0].set (1234); 
} // ()

// ...........................................................
void h ( const vector<A *> & v ) {
  cout << (*v[0]).get();
  (*v[0]).set(1234);
} // ()
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95

4 Answers4

38

Yes, a const vector provides access to its elements as if they were const, that is, it only gives you const references. In your second function, it's not the objects of type A that are const, but pointers to them. A pointer being const does not mean that the object the pointer is pointing to is const. To declare a pointer-to-const, use the type A const *.

isanae
  • 3,253
  • 1
  • 22
  • 47
lisyarus
  • 15,025
  • 3
  • 43
  • 68
  • 16
    No, the elements aren't `const`, that would be `vector`. You just can't get a non-const reference or pointer to them. – Mike Seymour Nov 21 '14 at 16:18
  • @lisyarus According to that, in the second function, the elements are const two times. One time because you can't change the array cells (vector is const), and another time because the elements themselves (pointers) are const for being in a const vector. Does this make sense? – cibercitizen1 Nov 21 '14 at 16:31
  • 2
    @cibercitizen1 No. In the second function `h`, you are not changing the pointer, but the `A` the pointer points to. And the pointer points to a non-const `A`, so that's not a problem. The container only gives you `const` access to the pointer saved in it, so you can't change what it's pointing to. – Oguk Nov 21 '14 at 16:41
  • 1
    @cibercitizen1 I don't follow... _"elements are const two times"_ ? A variable is either const or it isn't; there is no concept of being doubly const. Here the vector is const (i.e. you cannot add/remove elements to/from the vector or modify its size). A const vector will return a const reference to its elements via the `[] operator`. In the first case, you cannot change the value of a _const int&_. In the second case, you cannot change the value of a reference to a constant pointer, but you can change the value the pointer is pointed to. – Julian Nov 21 '14 at 16:46
  • 3
    @MikeSeymour As a side note: `vector` is not possible. `T` is not allowed to be `const` or `volatile`. See also this answer here: https://stackoverflow.com/a/6955332/11346320 – Zacryon Jul 08 '22 at 15:46
24

The first version

v[0].set (1234); 

does not compile because it tries to change the vector's first element returned to it by reference. The compiler thinks it's a change because set(int) is not marked const.

The second version, on the other hand, only reads from the vector

(*v[0]).set(1234);

and calls set on the result of the dereference of a constant reference to a pointer that it gets back.

When you call v[0] on a const vector, you get back a const reference to A. When element type is a pointer, calling set on it is OK. You could change the second example to

v[0]->set(1234);

and get the same result as before. This is because you get a reference to a pointer that is constant, but the item pointed to by that pointer is not constant.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
20

So a const object can only call const methods. That is:

class V {
  public:
    void foo() { ... }        // Can't be called
    void bar() const  { ... } // Can be called
};

So let's look at a vector's operator[]:

reference       operator[]( size_type pos );
const_reference operator[]( size_type pos ) const;

So when the vector object is const, it will return a const_reference.

About: (*v[0]).set(1234);

Let's break this down:

A * const & ptr = v[0];
A & val = *ptr;
val.set(1234);

Note that you have a constant pointer to variable data. So you can't change what is pointed at, but you can change the value that the pointer points at.

Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
8

Yes, because std::vector is a value-type rather than a reference type.

To put it more simply: An std::vector considers the values in its buffer as part of itself, so that changing them means changing the vector. This may be confusing if we only think of a vector as holding a pointer to an allocated buffer and the size: We don't change these two fields when we change elements in the buffer.

It's the opposite than for pointers, which are reference-types; if you change the pointed-to value you haven't changed the pointer itself.

The fact that std::vector is a value-type is a design choice - it's not something inherent in the C++ language. Thus, for example, the std::span class is also basically a pair of a pointer and a size, but an std::span can be const while you can still change the pointed-to elements. (There are other differences between spans and vectors.)

einpoklum
  • 118,144
  • 57
  • 340
  • 684