我在boost.range中发现了这个有趣的位:
提供独立式函数range_begin/end()
时,文档指出:
...
range_begin()
和range_end()
必须对两个const
重载 和mutable
参考参数。
确实,查看他们在end.hpp
中的默认值,我们看到:
//////////////////////////////////////////////////////////////////////
// pair
//////////////////////////////////////////////////////////////////////
template< typename Iterator >
inline Iterator range_end( const std::pair<Iterator,Iterator>& p )
{
return p.second;
}
template< typename Iterator >
inline Iterator range_end( std::pair<Iterator,Iterator>& p )
{
return p.second;
}
您将注意(文档中给出的示例也这样做),这两个版本都返回相同的Iterator
类型。
为什么我们首先需要超载?是为了使ADL工作吗?
您显然需要const &
版本,因为否则您的range_begin
对于constemified对象是无适合的。
不太明显的是为什么您还需要&
版本,但这很简单:如果您不提供它,那么您的自定义功能就比Boost自己的版本更糟糕。
这是一个简短的非启动示例:
namespace M {
struct S { };
void f(const S &);
}
namespace N {
template <typename T>
void f(T &);
template <typename T>
void g(T &t) { f(t); }
}
void h() {
M::S s {};
N::g(s);
}
在这里,在N::g<M::S>
的实例化期间,进行了无限制的呼叫f(t)
,并且参数t
具有M::S
类型。有两个候选人:N::f<M::S>
在同一名称空间中,但ADL也找到了M::f
。前者的参数是M::S &
。后者是const M::S &
。这意味着前者是更好的匹配,即使您真的希望使用命名空间中的版本M
中的版本。
额外的过载M::f(S &)
避免了此问题。