从常量成员函数返回迭代器

Returning iterator from constant member function

在下面的代码中,为什么 foo::func 的 return 类型是 vector<int>::const_iterator 而不是 vector<int>::iterator 尽管我 return 是 [=] 的对象16=].

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class foo
{
private:
  vector<int> ar;
public:
  foo()
  {
    ar.resize(10);
    iota(ar.begin(), ar.end(), 1);
  }
  auto func() const
  {
    return ar.begin() + 5;
  }
};

int main()
{
  foo x;
  cout<<boolalpha<<endl;
  auto it = x.func();
  cout<<is_same<decltype(it), vector<int>::iterator>::value<<endl;
  cout<<is_same<decltype(it), vector<int>::const_iterator>::value<<endl;
  return 0;
}

以上代码的输出是:

false
true

相反,如果我将 foo::func() 重新定义为

auto func()
{
    return ar.begin() + 5;
}

输出将是

true
false

为什么常量成员函数正在将 return 类型更改为常量? 我是否需要删除 const 关键字以使 return 类型成为 vector<int>::iterator 还是有其他方法?

iterator 指向它的数据成员之一,所以如果函数是 const 那么 iterator 也必须是 const 否则你可以改变数据包含在由 const 成员函数返回的 iterator 中的 class 中。

至少这是我的理解 - 如果其他人可以证实或否认这一点,我将不胜感激。

There are two std::vector::begin 取决于实例是否 const

foo::func() const 中,您正在尝试访问 ar,然后将其视为 const std::vector<int>,并且 std::vector::begin() const returns const_iterator.

当您在 foo::func() 中访问 ar 时,它将被视为 std::vector<int>,而不是 const,然后 begin 将引用 std::vector::begin()(又没有const),这不是同一个函数。

同样,您自己的 foo class 可以同时定义 foo::func 的两个版本:

auto func()
{ return ar.begin() + 5; }

auto func() const
{ return ar.begin() + 5; }

实例的常量将决定调用哪个版本:

foo x;
const foo y;

x.func();    // First version, get an iterator
y.func();    // Second version, get a const_iterator

const 声明的成员函数会影响 this 指针的类型。在 func() 中,this 的类型为 const foo*。因此,通过 const this 指针访问的所有成员类型本身都是 const,因此 ar 的隐式类型实际上是 const std::vector<int>。在 vector 的两个 begin() 重载中,唯一可行的重载是 const 重载,returns 一个 const_iterator.

当你重新定义 func() 为非 const 时,this 的类型就是 foo*,所以成员的类型 arstd::vector<int> 并且 begin() 的重载返回 iterator 是首选。

成员函数begin重载方式如下

iterator begin() noexcept;
const_iterator begin() const noexcept;

因此,使用限定符 const 声明的函数处理常量对象。这意味着常量对象的数据成员如果未声明为 mutable 也是常量。在这种情况下,为 returns const_iterator.

的数据成员 ar 调用第二个重载函数 begin