为什么在 C++11 中使用非成员开始和结束函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7593086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why use non-member begin and end functions in C++11?
提问by Jonathan M Davis
Every standard container has a begin
and end
method for returning iterators for that container. However, C++11 has apparently introduced free functions called std::begin
and std::end
which call the begin
and end
member functions. So, instead of writing
每个标准容器都有一个begin
andend
方法来返回该容器的迭代器。然而,C ++ 11显然已经引入自由函数调用std::begin
和std::end
该调用begin
和end
成员函数。所以,而不是写
auto i = v.begin();
auto e = v.end();
you'd write
你会写
auto i = std::begin(v);
auto e = std::end(v);
In his talk, Writing Modern C++, Herb Sutter says that you should always use the free functions now when you want the begin or end iterator for a container. However, he does not go into detail as to whyyou would want to. Looking at the code, it saves you all of one character. So, as far as the standard containers go, the free functions seem to be completely useless. Herb Sutter indicated that there were benefits for non-standard containers, but again, he didn't go into detail.
在他的演讲“编写现代 C++”中,Herb Sutter 说,当您想要容器的开始或结束迭代器时,您现在应该始终使用自由函数。但是,他没有详细说明您为什么要这样做。查看代码,它为您节省了一个字符。因此,就标准容器而言,免费功能似乎完全无用。Herb Sutter 表示非标准容器有好处,但同样,他没有详细说明。
So, the question is what exactly do the free function versions of std::begin
and std::end
do beyond calling their corresponding member function versions, and why would you want to use them?
那么,问题到底是什么做的免费功能的版本std::begin
,并std::end
做超出调用它们相应的成员函数的版本,你为什么要使用它们?
采纳答案by Matthieu M.
How do you call .begin()
and .end()
on a C-array ?
你如何在 C 数组上调用.begin()
和.end()
?
Free-functions allow for more generic programming because they can be added afterwards, on a data-structure you cannot alter.
自由函数允许更通用的编程,因为它们可以在之后添加到您无法更改的数据结构上。
回答by GreenScape
Consider the case when you have library that contain class:
考虑具有包含类的库的情况:
class SpecialArray;
it has 2 methods:
它有两种方法:
int SpecialArray::arraySize();
int SpecialArray::valueAt(int);
to iterate over it's values you need to inherit from this class and define begin()
and end()
methods for cases when
要迭代它的值,您需要从此类继承begin()
并end()
为以下情况定义和方法
auto i = v.begin();
auto e = v.end();
But if you always use
但是如果你总是使用
auto i = begin(v);
auto e = end(v);
you can do this:
你可以这样做:
template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, 0);
}
template <>
SpecialArrayIterator end(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, arr.arraySize());
}
where SpecialArrayIterator
is something like:
哪里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
};
now i
and e
can be legally used for iteration and accessing of values of SpecialArray
现在i
和e
可合法使用迭代和访问SpecialArray的值
回答by deft_code
Using the begin
and end
free functions adds one layer of indirection. Usually that is done to allow more flexibility.
使用begin
和end
free 函数会增加一层间接。通常这样做是为了提供更大的灵活性。
In this case I can think of a few uses.
在这种情况下,我可以想到一些用途。
The most obvious use is for C-arrays (not c pointers).
最明显的用途是用于 C 数组(不是 c 指针)。
Another is when trying to use a standard algorithm on a non-conforming container (ie the container is missing a .begin()
method). Assuming you can't just fix the container, the next best option is to overload the begin
function. Herb is suggesting you always use the begin
function to promote uniformity and consistency in your code. Instead of having to remember which containers support method begin
and which need function begin
.
另一种情况是尝试在不符合标准的容器上使用标准算法(即容器缺少.begin()
方法)。假设您不能只修复容器,那么下一个最佳选择是重载该begin
函数。Herb 建议您始终使用该begin
函数来提高代码的一致性和一致性。而不必记住哪些容器支持 methodbegin
哪些需要 function begin
。
As an aside, the next C++ rev should copy D's pseudo-member notation. If a.foo(b,c,d)
is not defined it instead tries foo(a,b,c,d)
. It's just a little syntactic sugar to help us poor humans who prefer subject then verb ordering.
顺便说一句,下一个 C++ rev 应该复制 D 的伪成员符号。如果a.foo(b,c,d)
未定义,则改为尝试foo(a,b,c,d)
。这只是一点点语法糖,可以帮助我们这些更喜欢主语而不是动词排序的可怜人。
回答by Nate
To answer your question, the free functions begin() and end() by default do nothing more than call the container's member .begin() and .end() functions. From <iterator>
, included automatically when you use any of the standard containers like <vector>
, <list>
, etc., you get:
为了回答您的问题,默认情况下,免费函数 begin() 和 end() 只会调用容器的成员 .begin() 和 .end() 函数。从<iterator>
,当您使用任何标准的容器,如自动包括<vector>
,<list>
等等,您可以:
template< class C >
auto begin( C& c ) -> decltype(c.begin());
template< class C >
auto begin( const C& c ) -> decltype(c.begin());
The second part of you question is why prefer the free functions if all they do is call the member functions anyway. That really depends on what kind of object v
is in your example code. If the type of v is a standard container type, like vector<T> v;
then it doesn't matter if you use the free or member functions, they do the same thing. If your object v
is more generic, like in the following code:
你问题的第二部分是如果他们所做的只是调用成员函数,为什么更喜欢自由函数。这实际上取决于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 .. */ }
}
Then using the member functions breaks your code for T = C arrays, C strings, enums, etc. By using the non-member functions, you advertise a more generic interface that people can easily extend. By using the free function interface:
然后使用成员函数会破坏 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 .. */ }
}
The code now works with T = C arrays and C strings. Now writing a small amount of adapter code:
该代码现在适用于 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); }
We can get your code to be compatible with iterable enums too. I think Herb's main point is that using the free functions is just as easy as using the member functions, and it gives your code backward compatibility with C sequence types and forward compatibility with non-stl sequence types (and future-stl types!), with low cost to other developers.
我们也可以让您的代码与可迭代枚举兼容。我认为 Herb 的主要观点是使用自由函数和使用成员函数一样简单,它使您的代码向后兼容 C 序列类型,并向前兼容非 stl 序列类型(和未来的 stl 类型!),对其他开发商来说成本低。
回答by joe_chip
One benefit of std::begin
and std::end
is that they serve as extension points
for implementing standard interface for external classes.
std::begin
和的好处之一std::end
是它们可以作为实现外部类标准接口的扩展点。
If you'd like to use CustomContainer
class with range-based for loop or template
function which expects .begin()
and .end()
methods, you'd obviously have to
implement those methods.
如果您想使用CustomContainer
带班的范围为基础的,其预计环或模板函数.begin()
和.end()
方法,你显然必须实现这些方法。
If the class does provide those methods, that's not a problem. When it doesn't, you'd have to modify it*.
如果该类确实提供了这些方法,那不是问题。如果没有,您必须修改它*。
This is not always feasible, for example when using external library, esspecially commercial and closed source one.
这并不总是可行的,例如在使用外部库时,尤其是商业和闭源库。
In such situations, std::begin
and std::end
come in handy, since one can provide
iterator API without modifying the class itself, but rather overloading free functions.
在这种情况下,std::begin
并std::end
派上用场,因为可以提供迭代器API,而无需修改类本身,而是重载自由功能。
Example:suppose that you'd like to implement count_if
function that takes a container
instead of a pair of iterators. Such code might look like this:
示例:假设您想实现count_if
一个接受容器而不是一对迭代器的函数。此类代码可能如下所示:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Now, for any class you'd like to use with this custom count_if
, you only have
to add two free functions, instead of modifying those classes.
现在,对于您想与此 custom 一起使用的任何类count_if
,您只需添加两个免费函数,而无需修改这些类。
Now, C++ has a mechanisim called Argument Dependent Lookup(ADL), which makes such approach even more flexible.
现在,C++ 有一种称为Argument Dependent Lookup(ADL)的机制,这使得这种方法更加灵活。
In short, ADL means, that when a compiler resolves an unqualified function (i. e.
function without namespace, like begin
instead of std::begin
), it will also
consider functions declared in namespaces of its arguments. For example:
简而言之,ADL 意味着,当编译器解析未限定的函数(即没有命名空间的函数,例如begin
代替std::begin
)时,它还将考虑在其参数的命名空间中声明的函数。例如:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
In this case, it doesn't matter that qualified names are some_lib::begin
and some_lib::end
- since CustomContainer
is in some_lib::
too, compiler will use those overloads in count_if
.
在这种情况下,不要紧,合格的名称是some_lib::begin
和some_lib::end
-因为CustomContainer
是在some_lib::
太,编译器将使用这些重载count_if
。
That's also the reason for having using std::begin;
and using std::end;
in count_if
.
This allows us to use unqualified begin
and end
, therefore allowing for ADL andallowing compiler to pick std::begin
and std::end
when no other alternatives are found.
这也是 haveusing std::begin;
和using std::end;
in的原因count_if
。这使我们能够使用不合格begin
和end
,因此允许ADL和允许编译器来接std::begin
和std::end
当发现没有其他的替代品。
We can eat the cookie and have the cookie - i. e. have a way to provide custom implementation
of begin
/end
while the compiler can fall back to standard ones.
我们可以吃掉 cookie 并拥有 cookie - 即有一种方法可以提供begin
/ 的自定义实现,end
而编译器可以回退到标准实现。
Some notes:
一些注意事项:
For the same reason, there are other similar functions:
std::rbegin
/rend
,std::size
andstd::data
.As other answers mentions,
std::
versions have overloads for naked arrays. That's useful, but is simply a special case of what I've described above.Using
std::begin
and friends is particularly good idea when writing template code, because this makes those templates more generic. For non-template you might just as well use methods, when applicable.
出于同样的原因,也有其他类似的功能:
std::rbegin
/rend
,std::size
和std::data
。正如其他答案所提到的,
std::
版本对裸数组有重载。这很有用,但只是我上面描述的一个特例。std::begin
在编写模板代码时使用和朋友是特别好的主意,因为这使这些模板更加通用。对于非模板,如果适用,您也可以使用方法。
P. S. I'm aware that this post is nearly 7 years old. I came across it because I wanted to answer a question which was marked as a duplicate and discovered that no answer here mentions ADL.
PS 我知道这篇文章已经有将近 7 年的历史了。我遇到它是因为我想回答一个被标记为重复的问题,发现这里没有答案提到 ADL。
回答by Christian Rau
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.
尽管非成员函数不会为标准容器提供任何好处,但使用它们可以强制实现更加一致和灵活的风格。如果您有时想扩展现有的非标准容器类,您宁愿定义自由函数的重载,而不是更改现有类的定义。因此,对于非标准容器,它们非常有用,并且始终使用自由函数使您的代码更加灵活,因为您可以更轻松地用非标准容器替换标准容器,并且底层容器类型对您的代码更加透明支持更广泛的容器实现。
But of course this always has to be weighted properly and over abstraction is not good either. Although using the free functions is not that much of an over-abstraction, it nevertheless breaks compatibility with C++03 code, which at this young age of C++11 might still be an issue for you.
但当然,这总是必须适当加权,过度抽象也不好。尽管使用免费函数并没有那么抽象,但它破坏了与 C++03 代码的兼容性,在 C++11 的这个年轻时代,这对您来说可能仍然是一个问题。
回答by Jonathan Mee
Ultimately the benefit is in code that is generalized such that it's container agnostic. It can operate on a std::vector
, an array, or a range without changes to the code itself.
最终的好处在于通用的代码,使其与容器无关。它可以在std::vector
不更改代码本身的情况下对、数组或范围进行操作。
Additionally, containers, even non-owned containers can be retrofitted such that they can also be used agnostically by code using non-member range based accessors.
此外,容器,甚至是非拥有的容器都可以进行改造,这样它们也可以被代码使用基于非成员范围的访问器以不可知的方式使用。
See herefor more detail.
有关更多详细信息,请参见此处。