为什么在C ++ 11中使用非成员的开始和结束函数?
每个标准容器都有一个用于返回该容器迭代器的begin
和end
方法。 但是,C ++ 11显然引入了称为std::begin
和std::end
自由函数,它们调用begin
和end
成员函数。 所以,而不是写作
auto i = v.begin(); auto e = v.end();
你会写
using std::begin; using std::end; auto i = begin(v); auto e = end(v);
在他的谈话中, Writing Modern C ++ ,Herb Sutter说,当你想要一个容器的开始或结束迭代器时,你应该总是使用自由函数。 但是,他没有详细说明为什么你想要。 看代码,它可以节省你所有的一个字符。 所以,就标准容器而言,免费function似乎完全没有用处。 Herb Sutter表示,对非标准容器有好处,但他没有详细说明。
所以,问题是std::begin
和std::end
的自由函数版本究竟做了什么,除了调用相应的成员函数版本之外,为什么要使用它们?
如何在C数组上调用.begin()
和.end()
?
自由函数允许更通用的编程,因为它们可以随后添加到不能更改的数据结构上。
考虑一下你拥有包含类的库的情况:
class SpecialArray;
它有两种方法:
int SpecialArray::arraySize(); int SpecialArray::valueAt(int);
迭代它需要从这个类inheritance的值,并定义begin()
和end()
方法
auto i = v.begin(); auto e = v.end();
但是,如果你总是使用
auto i = begin(v); auto e = end(v);
你可以这样做:
template <> SpecialArrayIterator begin(SpecialArray & arr) { return SpecialArrayIterator(&arr, 0); } template <> SpecialArrayIterator end(SpecialArray & arr) { return SpecialArrayIterator(&arr, arr.arraySize()); }
SpecialArrayIterator
是这样的:
class SpecialArrayIterator { SpecialArrayIterator(SpecialArray * p, int i) :index(i), parray(p) { } SpecialArrayIterator operator ++(); SpecialArrayIterator operator --(); SpecialArrayIterator operator ++(int); SpecialArrayIterator operator --(int); int operator *() { return parray->valueAt(index); } bool operator ==(SpecialArray &); // etc private: SpecialArray *parray; int index; // etc };
现在i
和e
可以合法地用于迭代和访问SpecialArray的值
使用begin
和end
免费function添加一层间接。 通常这样做是为了让更多的灵活性。
在这种情况下,我可以想到几个用途。
最明显的用途是用于C数组(而不是c指针)。
另一个是当试图在不合格的容器上使用标准algorithm(即容器缺less.begin()
方法)时。 假设你不能修正容器,下一个最好的select是重载begin
函数。 Herbbuild议您始终使用begin
函数来提高代码中的一致性和一致性。 而不必记住哪些容器支持方法begin
,哪些需要函数begin
。
另外,下一个C ++版本应该复制D的伪成员符号 。 如果没有定义a.foo(b,c,d)
则会尝试foo(a,b,c,d)
。 这只是一个小小的句法糖,可以帮助那些喜欢主语的穷人,然后是动词sorting。
为了回答你的问题,默认情况下,自由函数begin()和end()不需要调用容器的成员.begin()和.end()函数。 当使用<vector>
, <list>
等任何标准容器时,将自动包含<iterator>
,您将得到:
template< class C > auto begin( C& c ) -> decltype(c.begin()); template< class C > auto begin( const C& c ) -> decltype(c.begin());
你的第二部分问题是为什么更喜欢免费function,如果他们所做的就是调用成员函数。 这真的取决于你的代码中的对象v
是什么types的。 如果v的types是标准容器types,如vector<T> v;
那么使用免费或成员函数并不重要,它们也是一样的。 如果你的对象v
是更通用的,就像下面的代码一样:
template <class T> void foo(T& v) { auto i = v.begin(); auto e = v.end(); for(; i != e; i++) { /* .. do something with i .. */ } }
然后使用成员函数打破你的代码T = C数组,Cstring,枚举等。通过使用非成员函数,您可以宣传一个更通用的接口,人们可以轻松扩展。 通过使用免费的function界面:
template <class T> void foo(T& v) { auto i = begin(v); auto e = end(v); for(; i != e; i++) { /* .. do something with i .. */ } }
该代码现在可以与T = C数组和Cstring一起使用。 现在写入less量的适配器代码:
enum class color { RED, GREEN, BLUE }; static color colors[] = { color::RED, color::GREEN, color::BLUE }; color* begin(const color& c) { return begin(colors); } color* end(const color& c) { return end(colors); }
我们可以让你的代码与iterable枚举兼容。 我认为Herb的主要观点是使用自由函数就像使用成员函数一样简单,它使得你的代码与C序列types向后兼容,并且兼容非stl序列types(和future-stltypes)对其他开发者来说成本低。
鉴于非成员函数不能为标准容器提供任何好处,使用它们会强化更一致和灵活的风格。 如果你在某个时候想要扩展现有的非标准容器类,那么你应该定义自由函数的重载,而不是改变现有的类的定义。 所以对于非std容器他们是非常有用的,并且总是使用免费函数使得代码更加灵活,因为您可以更容易地用非std容器replacestd容器,并且底层容器types对代码更加透明支持更广泛的容器实现。
但是,这当然总是要权衡得好,抽象也不好。 尽pipe使用自由函数并不是一个过度抽象的东西,但它仍然会破坏C ++ 03代码的兼容性,而C ++ 03代码在C ++ 11的年轻时代可能仍然是一个问题。