STL容器中的const和non-const

4
STL向量模板定义了元素访问器,包括const和非const两种变体,例如:
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);}

编译器何时决定使用其中一个版本?向量本身没有被定义为const,存储在其中的元素也没有。因此,给定两个函数:

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

我理解第一个调用operator[]的const版本并返回const A。第二个调用非const版本并返回非const A?

对我而言,f()的第一个版本似乎是应该编写的“正确”版本,因为函数没有改变任何内容,但它可能让调用者惊讶,因为它返回了一个const A。如果我确实需要返回const A,那么我应该定义f()如下:

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

这将告诉编写调用者期望返回一个const。

所以这个额外的const是如何出现的?如果我使用boost::ptr_vector而不是std::vector,那么这个额外的const会应用于什么?数据?指针?还是两者都有?


3
你最好提出一个关于“const”的一般性问题。更好的方法是在你的C++教材中学习并尝试示例程序,然后可能会针对任何具体问题提问。你正在使用哪本教材? - Cheers and hth. - Alf
4个回答

6

这利用了函数重载规则中的一个棘手部分。首先,方法原型的括号后面的cv限定符就像参数上的cv限定符一样,但它们适用于隐式的this参数。在一种假设的C++变体中,您必须声明this,原型将如下所示:

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

因此,如果您调用方法的对象是const,则第二个重载是更接近的匹配并将被调用。如果不是,则将调用第一个重载。因此,如果您索引const容器,则会返回const_reference,如果您索引非const容器,则会返回reference。然后,const_reference对象强制执行指向其内部的只读特性。
有时,const_referenceconst reference相同,有时不同;对于更复杂的容器,reference是具有非平凡代码的类,该代码必须针对只读变体进行不同处理。标准始终使用const_reference,以便实现者可以在需要时自由地这样做。

const_reference 几乎从不与 const reference 相同,除非 value_type 已经是 const。如果 referenceint&,那么 const reference 也将是 int&,而不是 const int& - aschepler

1

const 在函数声明的末尾仅适用于非静态成员函数,并表示 *this 是 const 限定的。

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
}

在STL容器中,这种重载用于确定返回的引用或迭代器是否可以用于更改容器的元素。您无法通过一个带有const限定符的容器来更改元素。

1
在这个函数中:
A f(int i) const
    { return myVector[i]; }

你正在返回一个非const的A。由于你是通过值返回,operator[]返回的值(一个const引用)会被复制到一个新的A中。你“失去”了const,但这并不重要,因为你有一个全新的对象。相反,如果你的类看起来像这样:
struct B {
  const A& f(int i) const;
  A& f(int i);

private:
  vector<A> myVector;
};

然后您可以使用 const B 调用 f():

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

你的a将会是一个const引用。


0

返回一个const rvalue实际上是没有意义的-从一个const lvalue构造一个新值是完全合法的,这就是为什么第一种形式是有效的。想一想-如果你想从某个地方复制,那么无论你是否能够写入那个地方都无关紧要。

如果你返回一个引用,那么编译器将不允许你在const版本中返回A&。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接