为什么在C ++ 11中使用非成员的开始和结束函数?

每个标准容器都有一个用于返回该容器迭代器的beginend方法。 但是,C ++ 11显然引入了称为std::beginstd::end自由函数,它们调用beginend成员函数。 所以,而不是写作

 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::beginstd::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 }; 

现在ie可以合法地用于迭代和访问SpecialArray的值

使用beginend免费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的年轻时代可能仍然是一个问题。