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



Answers

考虑一下你拥有包含类的库的情况:

class SpecialArray;

它有2种方法:

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

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

Question

每个标准容器都有一个用于返回该容器的迭代器的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说,当你想要一个容器的开始或结束迭代器时,你应该总是使用自由函数。 但是,他没有详细说明为什么你想要。 看代码,它可以节省你所有的一个字符。 所以,就标准容器而言,免费功能似乎完全没有用处。 Herb Sutter表示,非标准容器有好处,但他没有详细说明。

所以,问题是std::beginstd::end的免费函数版本除了调用相应的成员函数版本之外,还有什么要使用它们?




为了回答你的问题,默认情况下,自由函数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()); 

你的第二部分问题是为什么更喜欢免费函数,如果他们所做的只是调用成员函数。 这真的取决于您的示例代码中的对象v类型。 如果v的类型是标准容器类型,如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数组,C字符串,枚举等的代码。通过使用非成员函数,您可以发布一个更通用的界面,人们可以轻松扩展。 通过使用免费功能界面:

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数组和C字符串。 现在编写少量的适配器代码:

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序列类型向后兼容,并且与非stl序列类型(以及future-stl类型!)向前兼容,对其他开发者来说成本低。






Links