const引用作为返回值如何在C ++中工作

时间:2016-11-29 21:26:24

标签: c++ c++11

我在一段时间后再次学习C ++,并且在将const引用理解为返回值时遇到问题。所以这就是我所拥有的:一个班级Foo,其中包含std::list成员:

class Foo
{
    std::list<int> mList;
    Foo() { mList.insert(mList.end(), { 1, 2, 3 }); }
    size_t size() const { return mList.size(); }
};

从班级foo创建Foo对象并调用foo.size()会返回3,这很好。现在我想要检索此列表mList,有两个要求:

  • 我不想让调用者修改原始列表;和
  • 我不想在检索列表时创建每个列表成员的副本。

因此,在阅读了有关此主题的内容后,我决定将const引用返回到列表中。因此,我将以下方法添加到Foo

const std::list<int>& getList() const { return mList; }

我的期望是这将返回对列表mList的引用,以便我可以访问此列表中的原始数据。但由于它是const引用,我还预计我将无法修改返回的列表/引用。

然而,玩了一下我发现了以下内容:

Foo foo;
cout << foo.size() << endl;  // returns 3
std::list<int> l = foo.getList();
l.clear();
cout << foo.size() << endl;  // returns 3 again

现在这让我感到惊讶,并引出了两个问题:

  1. 由于第二个cout再次返回3,因此对clear()的调用显然不会修改原始foo.mList对象。但是,如果我返回引用,为什么会这样呢?返回期间是否有副本?
  2. 如果我收到l.clear()(!)参考,为什么我可以首先致电const

2 个答案:

答案 0 :(得分:5)

getList会向列表中返回const&

如果你选择了const&列表,你可以做的一件事就是复制它。

std::list<int> l = foo.getList();

在这里你选择复制它。查看std::list<int> l?这不是一个参考。这是一个对象。

通过为其分配const&,您说&#34;请将列表内容复制到我的本地变量&#34;。

可以清除,编辑,修改此局部变量等。

如果您想要一些不容易被视为值的东西,您可以编写视图类型。

template<class It>
struct range_view_t {
  It b; It e;
  It begin() const { return b; }
  It end() const { return e; }
};
template<class C,
  class It = decltype( std::begin( std::declval<C&>() ) )
>
range_view_t<It> range_view( C& c ) {
  return {std::begin(c), std::end(c)};
}

现在range_view( some_list )会在列表中返回一个可迭代范围。

template<class X>
X const& as_const( X& x ) { return x; }

允许您确保范围是const:

auto r = range_view( as_const( list ) );

为列表提供了只读范围的迭代器。

template<class C>
using range_over = decltype( range_view( std::declval<C&>() ) );
template<class C>
using const_range_over = range_over<const C>;

class Foo
{
  std::list<int> mList;
  Foo() { mList.insert(mList.end(), { 1, 2, 3 }); }
  size_t size() const { return mList.size(); }
  const_range_over<std::list<int>>
  get_list() const {
    return range_view( mList );
  }
};

现在get_list会返回可在mList循环中使用的for(:)的范围视图。存储get_list返回值的副本不会复制任何内容,因为您只是复制某个视图。

此处几乎总是auto非常有用,因为范围视图的类型对用户来说无趣。

auto l = foo.get_list();

l没有.clear()方法。你可以这样做:

for( auto&& x : l )
  std::cout << x << "\n";

然而

最后,请注意std::list对于您可以描述的任何问题几乎总是错误的解决方案。

答案 1 :(得分:4)

执行std::list<int> l = foo.getList();后,您可以通过复制返回的引用来创建新列表l(即使getList()返回const引用,它也不会如果你愿意,禁止复制它。所以它后来被允许修改你制作的副本。

如果您执行const std::list<int>& l = foo.getList();,则不会进行任何复制,l仍然是对您无法修改的对象的const引用。