每个标准容器都有一个begin和end方法,用于返回该容器的迭代器。然而,c++ 11显然引入了名为std::begin和std::end的自由函数,它们调用begin和end成员函数。所以,与其写
auto i = v.begin();
auto e = v.end();
你会写
auto i = std::begin(v);
auto e = std::end(v);
在他的演讲《Writing Modern c++》中,Herb Sutter说,当你想要容器的开始或结束迭代器时,你应该总是使用free函数。然而,他并没有详细说明为什么你想这样做。看一下代码,它只节省了一个字符。因此,就标准容器而言,免费函数似乎完全无用。Herb Sutter指出,非标准容器也有好处,但他没有详细说明。
因此,问题是std::begin和std::end的自由函数版本除了调用它们对应的成员函数版本之外,还做了什么,以及为什么要使用它们?
Whereas the non-member functions don't provide any benefit for the standard containers, using them enforces a more consistent and flexible style. If you at some time want to extend an existing non-std container class, you'd rather define overloads of the free functions, instead of altering the existing class's definition. So for non-std containers they are very useful and always using the free functions makes your code more flexible in that you can substitute the std container by a non-std container more easily and the underlying container type is more transparent to your code as it supports a much wider variety of container implementations.
但当然,这总是需要适当地权衡,过度抽象也不好。尽管使用自由函数并没有那么多的过度抽象,但它仍然破坏了与c++ 03代码的兼容性,在c++ 11的年轻阶段,这对您来说可能仍然是一个问题。
使用begin和end free函数增加了一层间接性。通常这样做是为了有更大的灵活性。
在这种情况下,我可以想到一些用途。
最明显的用途是c数组(而不是c指针)。
另一种情况是试图在不符合标准的容器上使用标准算法(即容器缺少.begin()方法)。假设您不能只修复容器,那么下一个最佳选择就是重载begin函数。Herb建议总是使用begin函数来提高代码的一致性和一致性。而不必记住哪些容器支持方法开始,哪些容器需要函数开始。
顺便说一句,下一个c++版本应该复制D的伪成员表示法。如果a.foo(b,c,d)没有定义,则会尝试foo(a,b,c,d)。这只是一点语法糖,可以帮助我们这些喜欢主语顺序而不是动词顺序的可怜人。
考虑这样一种情况,当你有一个包含类的库:
class SpecialArray;
它有两种方法:
int SpecialArray::arraySize();
int SpecialArray::valueAt(int);
为了遍历它的值,你需要从这个类继承并定义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的值