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:: too中,编译器将在count_if中使用这些重载。
这也是使用std::begin的原因;并且使用std::end;在count_if。
这允许我们使用非限定的begin和end,因此允许ADL和
允许编译器选择std::begin和std::end,当没有其他选择时。
我们可以吃饼干和有饼干-即有一种方法提供自定义实现
的开始/结束,而编译器可以返回到标准的。
一些注意事项:
出于同样的原因,还有其他类似的函数:std::rbegin/rend,
Std::size和Std::data。
正如其他答案提到的,std::版本对裸数组有重载。这是有用的,
但这只是我上面所描述的一个特例。
在编写模板代码时,使用std::begin和friends是一个特别好的主意,
因为这使得这些模板更加通用。对于非模板,你可能只是
如果适用,也要使用方法。
附注:我知道这篇文章已经发布将近7年了。我偶然发现它是因为我想
回答一个被标记为重复的问题,发现这里没有答案提到ADL。