我在一段时间后再次学习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
现在这让我感到惊讶,并引出了两个问题:
cout
再次返回3,因此对clear()
的调用显然不会修改原始foo.mList
对象。但是,如果我返回引用,为什么会这样呢?返回期间是否有副本? l.clear()
(!)参考,为什么我可以首先致电const
?答案 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
引用。