的一个好处std::begin
和std::end
是它们充当实现外部类的标准接口的扩展点。
如果您想将CustomContainer
类与基于范围的for循环或模板函数一起使用,.begin()
并.end()
希望使用这些方法,则显然必须实现这些方法。
如果该类确实提供了这些方法,那不是问题。如果没有,则必须对其进行修改*。
这并不总是可行的,例如,在使用外部库(尤其是商业库和封闭源库)时。
在这种情况下,std::begin
并std::end
派上用场,因为可以提供迭代器API,而无需修改类本身,而是重载自由功能。
示例:假设您要实现一个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));
}
现在,对于您想要与此自定义一起使用的任何类count_if
,您只需添加两个免费函数,而无需修改这些类。
现在,C ++具有一种称为自变量依赖查找
(ADL)的机制,这使这种方法更加灵活。
简而言之,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);
在这种情况下,不要紧,合格的名称是some_lib::begin
和some_lib::end
-因为CustomContainer
是在some_lib::
太,编译器将使用这些重载count_if
。
这也是有原因using std::begin;
和using std::end;
在count_if
。这使我们能够使用不合格begin
和end
,因此允许ADL 和
允许编译器来接std::begin
和std::end
当发现没有其他的替代品。
我们可以吃cookie并拥有cookie-即有一种方法可以提供begin
/的自定义实现,end
而编译器可以使用标准的实现。
一些注意事项:
出于同样的原因,也有其他类似的功能:std::rbegin
/ rend
,
std::size
和std::data
。
正如其他答案所提到的,std::
版本具有裸数组的重载。这很有用,但这只是我上面描述的特例。
std::begin
在编写模板代码时,使用and friends是一个特别好的主意,因为这使这些模板更加通用。对于非模板,在适用时,您也可以使用方法。
附言:我知道这篇文章已有7年历史了。我碰到它是因为我想回答一个被标记为重复的问题,发现此处没有答案提及ADL。