每个标准容器都有一个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的自由函数版本除了调用它们对应的成员函数版本之外,还做了什么,以及为什么要使用它们?


当前回答

最终,这样做的好处在于代码是泛化的,因此它与容器无关。它可以操作std::vector、数组或范围,而无需更改代码本身。

此外,容器,甚至是非所有的容器都可以进行改造,以便代码使用基于非成员范围的访问器可以不可知地使用它们。

请看这里了解更多细节。

其他回答

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的年轻阶段,这对您来说可能仍然是一个问题。

如何在c数组上调用.begin()和.end() ?

自由函数允许更多的泛型编程,因为它们可以随后添加到您无法更改的数据结构上。

使用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的值

最终,这样做的好处在于代码是泛化的,因此它与容器无关。它可以操作std::vector、数组或范围,而无需更改代码本身。

此外,容器,甚至是非所有的容器都可以进行改造,以便代码使用基于非成员范围的访问器可以不可知地使用它们。

请看这里了解更多细节。