开发者

const and non-const in stl containers

The STL vector template defines element accessors as both const and non-const variants, for example:

reference operator[](size_type __n) 
    {return *(this->_M_impl._M_start + __n);}
const_reference operator[](size_type __n) const 
    {return *(this->_M_impl._M_start + __n);}

When does the compiler decide to use one version over the other? The vector itself is not defined as const, neither are the elements stored in it. So given two functions:

A f(int i) const
    { return myVector[i]; }
A f(int i)
    { return myVector[i]; }

My understanding is that the first would call the const version of operator[] and return a const A. The second calls the non-const version and returns a non-const A?

To 开发者_运维百科me, the first version of f() seems to be the "correct" one to write since the function isn't altering anything, but it is perhaps surprising to the caller that it returns a const A. Surely if I wanted a const A returned, I should need to define f() as:

const A f(int i) const    //Note the extra const at the start
    { return myVector[i]; }

Which would tell whoever is writing the caller to expect a const back.

So the extra const appears by magic? And what does the extra const get applied to if I'm using a boost::ptr_vector rather than std::vector? The data? The pointer? both?


This takes advantage of a tricky part of the function overload rules. First off, cv-qualifiers after the close parenthesis of a method prototype are just like cv-qualifiers on arguments, but they apply to the implicit this argument. In a hypothetical C++ variant where you had to declare this, the prototypes would be like so:

reference operator[](this_type this, size_type n);
const_reference operator[](const this_type this, size_type n);

Therefore, if the object you are calling the method on is const, the second overload is a closer match and will be called. If it's not, the first overload will be called. So you get a const_reference back if you index a const container, and you get a reference back if you index a non-const container. The const_reference object then enforces the read-only nature of the container it points into.

Sometimes const_reference is the same as const reference and sometimes it isn't; for the more complicated containers, a reference is a class with nontrivial code, and that code has to be different for the read-only variety. The standard consistently uses const_reference so that implementors have freedom to do that when they need to.


The const at the end of a function declaration applies only to non-static member functions, and means that *this is const-qualified.

struct A {
  void f(int i) const;
  void f(int i);
};

void g(const A& x, A& y) {
  x.f(); // calls const version
  y.f(); // calls non-const version
}

In STL containers, this sort of overload is used to determine whether the returned reference or iterator can be used to change the container's elements. You cannot change an element through a const qualified container.


In the function:

A f(int i) const
    { return myVector[i]; }

You are returning a non-const A. Since you are returning by value, the value returned by operator[] (a const reference) is copied into a new A. You are "losing" the const, but it doesn't matter because you have a whole new object. Rather, if your class looks like this:

struct B {
  const A& f(int i) const;
  A& f(int i);

private:
  vector<A> myVector;
};

and you call f() using a const B:

const B b;
const A& a = b.f();

your a will be a const reference.


Returning a const rvalue is effectively meaningless- it's perfectly legal to construct a new value from a const lvalue, which is why the first form is valid. Think about it- if you want to copy from somewhere, it doesn't matter if you can't write to that somewhere.

If you returned a reference, then the compiler would not allow you to return an A& in the const version.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜