gibraltar
gibraltar

Reputation: 1708

Copying member variable through const reference to a class

My understanding of const references to a class was that we cannot modify the state of the class i.e. cannot perform any action that modifies any of it's member variables. But consider the following code.

#include <iostream>
#include <vector>

struct Vec {
  Vec(std::vector<int>& v) : vec(v) {}
  Vec(const Vec& v) : vec(v.vec) {
    v.vec.resize(100);
  }

  std::vector<int>& vec;
};

int main() {
  std::vector<int> x;
  Vec v1(x);
  Vec v2(v1);
  v1.vec.resize(10, 0);
  v2.vec[5] = 8;
  std::cout << v1.vec[5] << std::endl;
  return 0;
}

I compiled this code using g++:4.8.3 with -Wall flag and it compiled. I have two questions.

  1. In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector<int>&. Then how does it get copied to type std::vector<int> ?
  2. The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?

Upvotes: 0

Views: 400

Answers (2)

QuestionC
QuestionC

Reputation: 10064

  1. In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector&. Then how does it get copied to type std::vector ?
  2. The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?

That's not really how const extends. You stick the const on the back of the type, not the front. Consider the following code...

struct foo {
    foo() {}

    int * i;
};

int main (void)
{
    foo my_foo;

    int * &a = my_foo.i;
    const int * &b = my_foo.i;
    int * const &c = my_foo.i;

    const foo const_foo;

    int * &d = const_foo.i;
    const int * &e = const_foo.i;
    int * const &f = const_foo.i;

    return 0;
}

foo.cpp: In function ‘int main()’:
foo.cpp:12: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
foo.cpp:16: error: invalid initialization of reference of type ‘int*&’ from expression of type ‘int* const’
foo.cpp:17: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int* const’

This shows that const_foo.i has type int * const, which is different from const int *. The type int * const makes no promises about its pointed at data not changing, just the pointer itself can't change.

In your example, v2.vec would have type std::vector<int> & const. But that type is meaningless (and illegal) because you can't change the alias of a reference anyhow. std::vector<int> is already const for this purpose.


It is possible to have const-ness inherit, but you have to explicitly code that rule. The following code will happily refuse to compile due to enforcing const restrictions by restricting callers to use a getter that makes the contract you are looking for...

#include <iostream>
#include <vector>

struct Vec {
    Vec(std::vector<int>& v) : _vec(v) {}
    Vec(const Vec& v) : _vec(v.vec()) {
        v.vec().resize(100);
    }

    // How to make const-ness inherit...
    std::vector<int> & vec() { return _vec; }
    std::vector<int> const & vec() const { return _vec; }

    private:
    std::vector<int>& _vec;
};

int main() {
    std::vector<int> x;
    Vec v1(x);
    Vec v2(v1);
    v1.vec().resize(10, 0);
    v2.vec()[5] = 8;
    std::cout << v1.vec()[5] << std::endl;
    return 0;
}

Once you start doing that you get into strange territory however, as it allows me to call std::vector<int> const & vec() const, save a 'data-const' reference to _vec, and then have other code change the data in vec, violating the const contract to the earlier code. There are a number of landmines out there, which may be why the language doesn't have this sort of const inheritance built-in.

Upvotes: 1

Sebastian Redl
Sebastian Redl

Reputation: 71909

The constness of a class applies to its member variables, but not to the referents of the member variables that are references. This is similar to pointer members, where the pointer would be const, but not what it points to.

Upvotes: 3

Related Questions